code wiki / _hdl_build / nx_kit_market.nx

nx_kit_market.nx source

↩ module page · 129 lines · 11299 B

1// nx_kit_market.nx -- the MARKET design family: a real storefront grammar for commerce briefs (shop, 2// store, boutique, catalog, merch). Fourth first-class family after marketing, Counsel and Journal. 3// 4// What a commerce page actually needs and the marketing grammar never had: a UTILITY BAR (search + cart) 5// above the masthead, CATEGORY CHIPS for browsing, a PRODUCT GRID whose cards carry a visual, a name, a 6// short line, a REAL PRICE and an add-to-cart action, and a SHIPPING/RETURNS/SECURE trust row -- the row 7// that decides whether a stranger buys. What it must NOT have: the SaaS gradient hero, the 3-up feature 8// card grid, the pricing-TIER band (tiers are for SaaS plans, not for beans). 9// 10// ★ Product visuals are GENERATED, never stock: each card gets a deterministic two-stop gradient panel 11// derived from the seed + card index, so a catalog looks like a catalog before a single photograph exists 12// (and the gen-img pipeline can replace the panel later without touching this grammar). 13// Token-driven from the generated palette, both themes, reduced-motion, doc tier included from the start 14// (the round-9 two-languages lesson, applied up front). Pure emit. license_tier: ORIGINAL 15import "nx_uigen_l1.nx" 16import "nx_kit_shell.nx" 17 18func km_styles(buf: *u8, off: i64, hue: i64) -> i64 { 19 var o: i64 = he_puts(buf, off, "<style>:root{" as *u8) 20 o = l1_tok(buf, o, "--km-ink:" as *u8, hue, 18, 13) 21 o = l1_tok(buf, o, "--km-bg:" as *u8, hue, 10, 98) 22 o = l1_tok(buf, o, "--km-card:" as *u8, hue, 8, 100) 23 o = l1_tok(buf, o, "--km-line:" as *u8, hue, 14, 88) 24 o = l1_tok(buf, o, "--km-quiet:" as *u8, hue, 10, 44) 25 o = l1_tok(buf, o, "--km-buy:" as *u8, hue, 64, 34) 26 o = l1_tok(buf, o, "--km-buy2:" as *u8, hue, 60, 44) 27 o = he_puts(buf, o, "}@media(prefers-color-scheme:dark){:root{" as *u8) 28 o = l1_tok(buf, o, "--km-ink:" as *u8, hue, 14, 92) 29 o = l1_tok(buf, o, "--km-bg:" as *u8, hue, 16, 9) 30 o = l1_tok(buf, o, "--km-card:" as *u8, hue, 14, 14) 31 o = l1_tok(buf, o, "--km-line:" as *u8, hue, 14, 24) 32 o = l1_tok(buf, o, "--km-quiet:" as *u8, hue, 10, 66) 33 o = l1_tok(buf, o, "--km-buy:" as *u8, hue, 60, 60) 34 o = l1_tok(buf, o, "--km-buy2:" as *u8, hue, 56, 68) 35 o = he_puts(buf, o, "}}" as *u8) 36 o = he_puts(buf, o, "body{background:var(--km-bg);color:var(--km-ink);font:16px/1.6 system-ui,-apple-system,\"Segoe UI\",sans-serif}" as *u8) 37 // utility bar sits ABOVE the shell header; the shell header becomes the shop masthead 38 o = he_puts(buf, o, ".km-util{background:var(--km-ink);color:var(--km-bg);font-size:.78rem}.km-util .wrap{display:flex;gap:16px;align-items:center;justify-content:space-between;padding:9px 24px;flex-wrap:wrap}.km-util form{display:flex;gap:0;flex:1;max-width:420px}.km-util input{flex:1;min-width:120px;font:inherit;padding:7px 12px;border:0;border-radius:2px 0 0 2px;background:var(--km-bg);color:var(--km-ink)}.km-util button{font:inherit;font-weight:640;border:0;border-radius:0 2px 2px 0;padding:7px 15px;background:var(--km-buy);color:#fff;cursor:pointer}.km-util a{color:var(--km-bg);text-decoration:none;font-weight:640;letter-spacing:.04em}" as *u8) 39 o = kx_shell_reset(buf, o) 40 o = he_puts(buf, o, "header.site{background:var(--km-bg);border-bottom:1px solid var(--km-line)}header.site .bar{padding:16px 24px}header.site .brand{font-weight:800;letter-spacing:-.02em;color:var(--km-ink)}header.site .brand .dot{border-radius:3px;background:var(--km-buy)}header.site nav.top a{font-size:.86rem;font-weight:600;color:var(--km-quiet)}header.site nav.top a:hover{color:var(--km-ink)}" as *u8) 41 o = he_puts(buf, o, "footer.site{border-top:1px solid var(--km-line)}" as *u8) 42 // category chips 43 o = he_puts(buf, o, ".km-chips{padding:18px 0 4px}.km-chips .wrap{display:flex;gap:9px;flex-wrap:wrap}.km-chip{display:inline-block;border:1px solid var(--km-line);background:var(--km-card);border-radius:999px;padding:7px 15px;font-size:.82rem;font-weight:600;text-decoration:none;color:var(--km-ink)}.km-chip:hover{border-color:var(--km-buy);color:var(--km-buy)}" as *u8) 44 // promo strip -- one line of value, not a gradient hero 45 o = he_puts(buf, o, ".km-promo{background:var(--km-card);border-top:1px solid var(--km-line);border-bottom:1px solid var(--km-line);margin-top:14px}.km-promo .wrap{display:flex;gap:14px;align-items:baseline;flex-wrap:wrap;padding:16px 24px}.km-promo b{font-size:1.05rem}.km-promo span{color:var(--km-quiet);font-size:.92rem}" as *u8) 46 // product grid 47 o = he_puts(buf, o, ".km-shop{padding:clamp(24px,4vw,44px) 0}.km-shop h2{font-size:1.22rem;font-weight:750;letter-spacing:-.01em;margin:0 0 4px}.km-shop .km-sub{color:var(--km-quiet);margin:0 0 22px;font-size:.95rem}" as *u8) 48 o = he_puts(buf, o, ".km-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:clamp(14px,2vw,24px)}" as *u8) 49 o = he_puts(buf, o, ".km-item{background:var(--km-card);border:1px solid var(--km-line);border-radius:10px;overflow:hidden;display:flex;flex-direction:column}.km-shot{aspect-ratio:4/3;display:block}.km-item h3{font-size:1rem;font-weight:680;margin:14px 15px 3px}.km-item p{color:var(--km-quiet);font-size:.86rem;margin:0 15px 10px;flex:1}.km-row{display:flex;align-items:center;justify-content:space-between;gap:10px;margin:0 15px 15px}.km-price{font-weight:750;font-size:1.06rem;font-variant-numeric:tabular-nums}.km-add{font:inherit;font-size:.83rem;font-weight:660;border:0;border-radius:6px;padding:9px 14px;background:var(--km-buy);color:#fff;cursor:pointer;text-decoration:none}.km-add:hover{background:var(--km-buy2)}.km-add:focus-visible{outline:3px solid var(--km-buy2);outline-offset:2px}" as *u8) 50 // shipping / returns / secure -- the row that closes the sale 51 o = he_puts(buf, o, ".km-trust{border-top:1px solid var(--km-line);margin-top:8px;padding:22px 0 30px}.km-trust .wrap{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:18px}.km-trust b{display:block;font-size:.93rem}.km-trust span{color:var(--km-quiet);font-size:.85rem}" as *u8) 52 o = he_puts(buf, o, "@media(prefers-reduced-motion:reduce){.km-item,.km-add{transition:none;animation:none}}</style>" as *u8) 53 return o 54} 55 56// utility bar (search + cart). Emitted BEFORE the shell header by the page composer. 57func km_util(buf: *u8, off: i64, ship: *u8) -> i64 { 58 var o: i64 = he_puts(buf, off, "<div class=\"km-util\"><div class=\"wrap\"><span>" as *u8) 59 o = he_puts(buf, o, ship) 60 o = he_puts(buf, o, "</span><form role=\"search\" method=\"get\" action=\"index.html\"><input type=\"search\" name=\"q\" aria-label=\"Search the shop\" placeholder=\"Search the shop\"><button type=\"submit\">Search</button></form><a href=\"contact.html\">Cart (0)</a></div></div>" as *u8) 61 return o 62} 63func km_chips_open(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "<nav class=\"km-chips\" aria-label=\"Categories\"><div class=\"wrap\">" as *u8) } 64func km_chip(buf: *u8, off: i64, label: *u8) -> i64 { 65 var o: i64 = he_puts(buf, off, "<a class=\"km-chip\" href=\"index.html\">" as *u8) 66 o = he_puts(buf, o, label) 67 return he_puts(buf, o, "</a>" as *u8) 68} 69func km_chips_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</div></nav>" as *u8) } 70func km_promo(buf: *u8, off: i64, headline: *u8, sub: *u8) -> i64 { 71 var o: i64 = he_puts(buf, off, "<section class=\"km-promo\"><div class=\"wrap\"><b>" as *u8) 72 o = he_puts(buf, o, headline) 73 o = he_puts(buf, o, "</b><span>" as *u8) 74 o = he_puts(buf, o, sub) 75 return he_puts(buf, o, "</span></div></section>" as *u8) 76} 77func km_shop_open(buf: *u8, off: i64, h2: *u8, sub: *u8) -> i64 { 78 var o: i64 = he_puts(buf, off, "<section class=\"km-shop\"><div class=\"wrap\"><h2>" as *u8) 79 o = he_puts(buf, o, h2) 80 o = he_puts(buf, o, "</h2><p class=\"km-sub\">" as *u8) 81 o = he_puts(buf, o, sub) 82 return he_puts(buf, o, "</p><div class=\"km-grid\">" as *u8) 83} 84// one product card. The visual is a GENERATED two-stop gradient derived from hue + index -- a catalog 85// that looks like a catalog before any photograph exists, and never a stock image. 86func km_item(buf: *u8, off: i64, hue: i64, idx: i64, name: *u8, desc: *u8, price: *u8) -> i64 { 87 // a CATALOG must look like one shop, not a rainbow: drift the hue only slightly per card (±11deg) 88 // and carry the variation in LIGHTNESS instead. The render caught a coffee shop selling a teal bag. 89 var h1: i64 = hue + idx * 11 90 while h1 >= 360 { h1 = h1 - 360 } 91 var h2: i64 = h1 + 14 92 while h2 >= 360 { h2 = h2 - 360 } 93 let l1v: i64 = 76 - (idx % 3) * 7 94 let l2v: i64 = 50 - (idx % 2) * 6 95 var o: i64 = he_puts(buf, off, "<article class=\"km-item\"><svg class=\"km-shot\" viewBox=\"0 0 120 90\" role=\"img\" aria-label=\"\" preserveAspectRatio=\"none\"><defs><linearGradient id=\"g" as *u8) 96 o = l1_put_u8(buf, o, idx) 97 o = he_puts(buf, o, "\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"><stop offset=\"0\" stop-color=\"" as *u8) 98 o = l1_rgb(buf, o, h1, 38, l1v) 99 o = he_puts(buf, o, "\"/><stop offset=\"1\" stop-color=\"" as *u8) 100 o = l1_rgb(buf, o, h2, 48, l2v) 101 o = he_puts(buf, o, "\"/></linearGradient></defs><rect width=\"120\" height=\"90\" fill=\"url(&#35;g" as *u8) 102 o = l1_put_u8(buf, o, idx) 103 o = he_puts(buf, o, ")\"/></svg><h3>" as *u8) 104 o = he_puts(buf, o, name) 105 o = he_puts(buf, o, "</h3><p>" as *u8) 106 o = he_puts(buf, o, desc) 107 o = he_puts(buf, o, "</p><div class=\"km-row\"><span class=\"km-price\">" as *u8) 108 o = he_puts(buf, o, price) 109 o = he_puts(buf, o, "</span><a class=\"km-add\" href=\"contact.html\">Add to cart</a></div></article>" as *u8) 110 return o 111} 112func km_shop_close(buf: *u8, off: i64) -> i64 { return he_puts(buf, off, "</div></div></section>" as *u8) } 113func km_trust(buf: *u8, off: i64, a1: *u8, b1: *u8, a2: *u8, b2: *u8, a3: *u8, b3: *u8) -> i64 { 114 var o: i64 = he_puts(buf, off, "<section class=\"km-trust\"><div class=\"wrap\"><div><b>" as *u8) 115 o = he_puts(buf, o, a1); o = he_puts(buf, o, "</b><span>" as *u8); o = he_puts(buf, o, b1) 116 o = he_puts(buf, o, "</span></div><div><b>" as *u8) 117 o = he_puts(buf, o, a2); o = he_puts(buf, o, "</b><span>" as *u8); o = he_puts(buf, o, b2) 118 o = he_puts(buf, o, "</span></div><div><b>" as *u8) 119 o = he_puts(buf, o, a3); o = he_puts(buf, o, "</b><span>" as *u8); o = he_puts(buf, o, b3) 120 return he_puts(buf, o, "</span></div></div></section>" as *u8) 121} 122// doc tier so about/contact stay in-family 123func km_doc_styles(buf: *u8, off: i64) -> i64 { 124 var o: i64 = he_puts(buf, off, "<style>.doc h1{font-weight:800;letter-spacing:-.022em}.doc .lede{margin:0 0 1.1em;max-width:60ch;color:var(--km-quiet)}" as *u8) 125 o = he_puts(buf, o, ".doc .callout{background:var(--km-card);border-left:3px solid var(--km-buy);border-radius:0 8px 8px 0}" as *u8) 126 o = he_puts(buf, o, ".field input,.field textarea{border-radius:6px;border:1px solid var(--km-line);background:var(--km-card)}.field input:focus-visible,.field textarea:focus-visible{outline:none;border-color:var(--km-buy);box-shadow:0 0 0 3px var(--km-line)}" as *u8) 127 o = he_puts(buf, o, ".btnp{background:var(--km-buy);border-radius:6px;box-shadow:none}.btnp:hover{background:var(--km-buy2);transform:none}</style>" as *u8) 128 return o 129}