@litfamily/litopencode 1.0.30 → 1.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README-Ko-KR.md +6 -6
  3. package/README.md +6 -6
  4. package/dist/activation-probe.js +1 -0
  5. package/dist/activation-prompt-utils.d.ts +1 -1
  6. package/dist/activation-reentry.d.ts +20 -0
  7. package/dist/activation-reentry.js +110 -1
  8. package/dist/activation-routing.d.ts +2 -1
  9. package/dist/activation-routing.js +100 -11
  10. package/dist/activation-workflow-prompts.d.ts +4 -0
  11. package/dist/activation-workflow-prompts.js +30 -0
  12. package/dist/activation.d.ts +1 -1
  13. package/dist/activation.js +44 -5
  14. package/dist/adhd-next.d.ts +51 -0
  15. package/dist/adhd-next.js +150 -0
  16. package/dist/cli/managed-skill-assets.d.ts +1 -1
  17. package/dist/cli/managed-skill-assets.js +7 -1
  18. package/dist/cli/native-skill-integrity.js +1 -1
  19. package/dist/cli.js +7 -1
  20. package/dist/commands.d.ts +28 -1
  21. package/dist/commands.js +48 -1
  22. package/dist/features.d.ts +80 -1
  23. package/dist/features.js +105 -1
  24. package/dist/ledger.js +12 -1
  25. package/dist/secret-shapes.js +5 -5
  26. package/dist/skills.d.ts +22 -1
  27. package/dist/skills.js +36 -0
  28. package/dist/start-work-typed.d.ts +34 -0
  29. package/dist/start-work-typed.js +156 -0
  30. package/dist/state.d.ts +2 -0
  31. package/dist/state.js +2 -0
  32. package/dist/tools.js +22 -2
  33. package/dist/tui/mission-band.d.ts +20 -1
  34. package/dist/tui/mission-band.js +73 -16
  35. package/dist/tui/mission-looks.d.ts +38 -0
  36. package/dist/tui/mission-looks.js +73 -0
  37. package/dist/tui/mission-model.d.ts +2 -0
  38. package/dist/tui/mission-model.js +3 -1
  39. package/dist/tui/mission-rainbow.d.ts +33 -0
  40. package/dist/tui/mission-rainbow.js +90 -0
  41. package/dist/tui/mission-source.d.ts +48 -0
  42. package/dist/tui/mission-source.js +184 -2
  43. package/dist/tui/mission-text.d.ts +2 -0
  44. package/dist/tui/mission-text.js +9 -1
  45. package/dist/tui/mission-threads.d.ts +50 -0
  46. package/dist/tui/mission-threads.js +119 -0
  47. package/dist/tui.js +84 -6
  48. package/dist/uiux-visual-catalog.js +8 -1
  49. package/dist/v2/session-hooks.js +38 -5
  50. package/dist/workflow-stack.d.ts +53 -0
  51. package/dist/workflow-stack.js +196 -0
  52. package/docs/assets/readme/badge-version.svg +1 -1
  53. package/docs/assets/skills/lit-schematic.webp +0 -0
  54. package/docs/reference-Ko-KR.md +30 -2
  55. package/docs/reference.md +34 -2
  56. package/package.json +1 -1
  57. package/skills/frontend-ui-ux/SKILL.md +3 -2
  58. package/skills/frontend-ui-ux/kit/components.css +414 -0
  59. package/skills/frontend-ui-ux/kit/fonts.json +22 -0
  60. package/skills/frontend-ui-ux/kit/layouts/landing.html +164 -0
  61. package/skills/frontend-ui-ux/kit/layouts/settings.html +114 -0
  62. package/skills/frontend-ui-ux/kit/layouts/workspace.html +115 -0
  63. package/skills/frontend-ui-ux/kit/styles/computational-brutalism.json +227 -0
  64. package/skills/frontend-ui-ux/kit/styles/field-notes.json +205 -0
  65. package/skills/frontend-ui-ux/kit/styles/graphite.json +200 -0
  66. package/skills/frontend-ui-ux/kit/styles/meadow.json +199 -0
  67. package/skills/frontend-ui-ux/kit/styles/neo-brutalism.json +222 -0
  68. package/skills/frontend-ui-ux/kit/ui.js +226 -0
  69. package/skills/frontend-ui-ux/references/brand-and-imagery.md +2 -2
  70. package/skills/frontend-ui-ux/references/complete-contract.md +2 -1
  71. package/skills/frontend-ui-ux/references/craft-floor.md +6 -2
  72. package/skills/frontend-ui-ux/references/default-editorial-pixel.json +3 -0
  73. package/skills/frontend-ui-ux/references/house-styles.md +236 -0
  74. package/skills/frontend-ui-ux/references/motion-guide.md +2 -2
  75. package/skills/frontend-ui-ux/references/production.md +3 -1
  76. package/skills/frontend-ui-ux/references/slop-register.md +4 -4
  77. package/skills/frontend-ui-ux/scripts/design-lock.mjs +400 -0
  78. package/skills/frontend-ui-ux/scripts/pixel-art.mjs +60 -0
  79. package/skills/frontend-ui-ux/scripts/probe-lock.mjs +235 -0
  80. package/skills/frontend-ui-ux/scripts/probe-page.mjs +34 -12
  81. package/skills/frontend-ui-ux/scripts/probe-static.mjs +16 -3
  82. package/skills/frontend-ui-ux/scripts/probe-thresholds.json +8 -2
  83. package/skills/frontend-ui-ux/scripts/probe.mjs +35 -7
  84. package/skills/lit-adhd/SKILL.md +208 -0
  85. package/skills/lit-adhd/references/brief-format.md +43 -0
  86. package/skills/lit-adhd/references/card-examples.md +82 -0
  87. package/skills/lit-adhd/references/status-taxonomy.md +29 -0
  88. package/skills/lit-adhd/scripts/rewind.mjs +835 -0
  89. package/skills/lit-diagram-drawer/SKILL.md +4 -3
  90. package/skills/lit-diagram-drawer/references/style-guide.md +20 -11
  91. package/skills/lit-diagram-drawer/references/type-architecture.md +3 -3
  92. package/skills/lit-diagram-drawer/references/type-dependency.md +1 -1
  93. package/skills/lit-diagram-drawer/references/type-tree.md +3 -3
  94. package/skills/lit-diagram-drawer/references/type-wardley.md +1 -1
  95. package/skills/lit-diagram-drawer/references/verifier-guide.md +1 -0
  96. package/skills/lit-diagram-drawer/scripts/craft-thresholds.json +1 -2
  97. package/skills/lit-diagram-drawer/scripts/visual-quality.mjs +1 -2
  98. package/skills/lit-docx/SKILL.md +2 -1
  99. package/skills/lit-handoff/SKILL.md +2 -0
  100. package/skills/lit-hijack/SKILL.md +422 -0
  101. package/skills/lit-hijack/references/capture-and-provenance.md +87 -0
  102. package/skills/lit-hijack/references/clean-room-build.md +111 -0
  103. package/skills/lit-hijack/references/distil-and-licence.md +100 -0
  104. package/skills/lit-hijack/references/gap-and-port-design.md +126 -0
  105. package/skills/lit-hijack/references/parity-and-report.md +110 -0
  106. package/skills/lit-hijack/scripts/hijack.mjs +993 -0
  107. package/skills/lit-plan/SKILL.md +2 -0
  108. package/skills/lit-pptx/SKILL.md +27 -11
  109. package/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf +0 -0
  110. package/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Regular.ttf +0 -0
  111. package/skills/lit-pptx/fonts/jetbrains-mono/OFL.txt +93 -0
  112. package/skills/lit-pptx/fonts/jetbrains-mono/SOURCE.md +17 -0
  113. package/skills/lit-pptx/references/density-and-fill.md +9 -3
  114. package/skills/lit-pptx/references/direction-step.md +21 -6
  115. package/skills/lit-pptx/references/examples/04-lecture-chalk-ko.md +3 -6
  116. package/skills/lit-pptx/references/examples/05-research-talk-paper-en.md +1 -5
  117. package/skills/lit-pptx/references/examples/08-reliability-keynote-night-en.md +0 -4
  118. package/skills/lit-pptx/references/examples/09-status-update-ledger-en.md +2 -2
  119. package/skills/lit-pptx/references/examples/11-forecast-review-night-ko.md +4 -7
  120. package/skills/lit-pptx/references/examples/13-engineering-review-computational-brutalism-ko.md +173 -0
  121. package/skills/lit-pptx/references/examples/14-launch-pitch-neo-brutalism-ko.md +154 -0
  122. package/skills/lit-pptx/references/examples/15-design-lecture-bauhaus-ko.md +139 -0
  123. package/skills/lit-pptx/references/examples/16-community-programme-riso-ko.md +158 -0
  124. package/skills/lit-pptx/references/examples/17-public-data-briefing-newsroom-ko.md +179 -0
  125. package/skills/lit-pptx/references/layout-families.md +65 -47
  126. package/skills/lit-pptx/references/title-treatments.md +83 -134
  127. package/skills/lit-pptx/references/tonalities/atlas.md +3 -3
  128. package/skills/lit-pptx/references/tonalities/bauhaus.md +140 -0
  129. package/skills/lit-pptx/references/tonalities/chalk.md +7 -6
  130. package/skills/lit-pptx/references/tonalities/computational-brutalism.md +150 -0
  131. package/skills/lit-pptx/references/tonalities/gazette.md +1 -1
  132. package/skills/lit-pptx/references/tonalities/ledger.md +1 -1
  133. package/skills/lit-pptx/references/tonalities/neo-brutalism.md +161 -0
  134. package/skills/lit-pptx/references/tonalities/newsroom.md +144 -0
  135. package/skills/lit-pptx/references/tonalities/night.md +1 -1
  136. package/skills/lit-pptx/references/tonalities/paper.md +2 -2
  137. package/skills/lit-pptx/references/tonalities/riso.md +137 -0
  138. package/skills/lit-pptx/references/tonalities/signal.md +3 -3
  139. package/skills/lit-pptx/references/tonalities/studio.md +7 -6
  140. package/skills/lit-pptx/run.mjs +2 -1
  141. package/skills/lit-pptx/scripts/compile-deck.js +1 -1
  142. package/skills/lit-pptx/scripts/craft-thresholds.json +4 -8
  143. package/skills/lit-pptx/scripts/craft_extras.py +56 -3
  144. package/skills/lit-pptx/scripts/deck_output.py +75 -90
  145. package/skills/lit-pptx/scripts/embed_fonts.py +13 -1
  146. package/skills/lit-pptx/scripts/layout_inventory.py +12 -5
  147. package/skills/lit-pptx/scripts/lib/grid-resolver.js +101 -110
  148. package/skills/lit-pptx/scripts/lib/layout-resolver.js +9 -15
  149. package/skills/lit-pptx/scripts/lib/render-pack.js +373 -566
  150. package/skills/lit-pptx/scripts/lib/slide-grid.js +104 -0
  151. package/skills/lit-pptx/scripts/lib/template-registry.js +57 -6
  152. package/skills/lit-pptx/scripts/qa_deck.py +10 -11
  153. package/skills/lit-pptx/scripts/render_pages.py +118 -0
  154. package/skills/lit-pptx/templates/tonalities/atlas/pack.yaml +1 -1
  155. package/skills/lit-pptx/templates/tonalities/bauhaus/pack.yaml +98 -0
  156. package/skills/lit-pptx/templates/tonalities/chalk/pack.yaml +1 -1
  157. package/skills/lit-pptx/templates/tonalities/computational-brutalism/pack.yaml +99 -0
  158. package/skills/lit-pptx/templates/tonalities/gazette/pack.yaml +1 -1
  159. package/skills/lit-pptx/templates/tonalities/ledger/pack.yaml +1 -1
  160. package/skills/lit-pptx/templates/tonalities/neo-brutalism/pack.yaml +107 -0
  161. package/skills/lit-pptx/templates/tonalities/newsroom/pack.yaml +99 -0
  162. package/skills/lit-pptx/templates/tonalities/night/pack.yaml +1 -1
  163. package/skills/lit-pptx/templates/tonalities/paper/pack.yaml +1 -1
  164. package/skills/lit-pptx/templates/tonalities/riso/pack.yaml +99 -0
  165. package/skills/lit-pptx/templates/tonalities/signal/pack.yaml +2 -2
  166. package/skills/lit-pptx/templates/tonalities/studio/pack.yaml +1 -1
  167. package/skills/lit-recap/SKILL.md +4 -0
  168. package/skills/lit-schematic/SKILL.md +118 -0
  169. package/skills/lit-schematic/examples/iso-heat-exchanger/build.mjs +38 -0
  170. package/skills/lit-schematic/examples/iso-heat-exchanger/scene.mjs +133 -0
  171. package/skills/lit-schematic/examples/journal-adsorption-rig/build.mjs +87 -0
  172. package/skills/lit-schematic/examples/pid-dosing-skid/build.mjs +85 -0
  173. package/skills/lit-schematic/references/cutaway-outputs.md +70 -0
  174. package/skills/lit-schematic/references/isometric-craft.md +101 -0
  175. package/skills/lit-schematic/references/journal-figures.md +72 -0
  176. package/skills/lit-schematic/references/kit-api.md +134 -0
  177. package/skills/lit-schematic/references/pfd-and-layout.md +53 -0
  178. package/skills/lit-schematic/references/pid-drafting.md +103 -0
  179. package/skills/lit-schematic/references/pid-symbols.md +163 -0
  180. package/skills/lit-schematic/references/verification.md +100 -0
  181. package/skills/lit-schematic/scripts/doctor.mjs +57 -0
  182. package/skills/lit-schematic/scripts/lib/figure.mjs +81 -0
  183. package/skills/lit-schematic/scripts/lib/fonts.mjs +25 -0
  184. package/skills/lit-schematic/scripts/lib/geom.mjs +113 -0
  185. package/skills/lit-schematic/scripts/lib/interactive.mjs +292 -0
  186. package/skills/lit-schematic/scripts/lib/isa.mjs +115 -0
  187. package/skills/lit-schematic/scripts/lib/iso.mjs +719 -0
  188. package/skills/lit-schematic/scripts/lib/legend-sheet.mjs +162 -0
  189. package/skills/lit-schematic/scripts/lib/pid.mjs +937 -0
  190. package/skills/lit-schematic/scripts/lib/png.mjs +132 -0
  191. package/skills/lit-schematic/scripts/lib/qa-core.mjs +658 -0
  192. package/skills/lit-schematic/scripts/lib/route.mjs +218 -0
  193. package/skills/lit-schematic/scripts/lib/sheet.mjs +890 -0
  194. package/skills/lit-schematic/scripts/lib/svg.mjs +82 -0
  195. package/skills/lit-schematic/scripts/lib/theme.mjs +115 -0
  196. package/skills/lit-schematic/scripts/motion.mjs +129 -0
  197. package/skills/lit-schematic/scripts/qa.mjs +92 -0
  198. package/skills/lit-schematic/scripts/render.mjs +233 -0
  199. package/skills/lit-schematic/scripts/scaffold.mjs +60 -0
  200. package/skills/lit-scientific-visualization/SKILL.md +27 -4
  201. package/skills/lit-typographic-motion/SKILL.md +19 -17
  202. package/skills/lit-typographic-motion/THIRD_PARTY_NOTICES +9 -0
  203. package/skills/lit-typographic-motion/engine/accents.mjs +6 -0
  204. package/skills/lit-typographic-motion/engine/assets.mjs +282 -0
  205. package/skills/lit-typographic-motion/engine/blender/runner.py +228 -0
  206. package/skills/lit-typographic-motion/engine/blender.mjs +126 -0
  207. package/skills/lit-typographic-motion/engine/board.mjs +138 -0
  208. package/skills/lit-typographic-motion/engine/cover/assets.mjs +11 -8
  209. package/skills/lit-typographic-motion/engine/cover/capture.mjs +6 -6
  210. package/skills/lit-typographic-motion/engine/cover/render.mjs +20 -7
  211. package/skills/lit-typographic-motion/engine/cover/treatment.mjs +30 -3
  212. package/skills/lit-typographic-motion/engine/fill.mjs +54 -0
  213. package/skills/lit-typographic-motion/engine/gallery.mjs +118 -0
  214. package/skills/lit-typographic-motion/engine/house.mjs +42 -0
  215. package/skills/lit-typographic-motion/engine/look.mjs +65 -8
  216. package/skills/lit-typographic-motion/engine/loudness.mjs +101 -0
  217. package/skills/lit-typographic-motion/engine/scaffold.mjs +219 -0
  218. package/skills/lit-typographic-motion/engine/score.mjs +594 -0
  219. package/skills/lit-typographic-motion/engine/sound.mjs +9 -57
  220. package/skills/lit-typographic-motion/engine/stage/capture.mjs +23 -9
  221. package/skills/lit-typographic-motion/engine/stage/clock.js +8 -2
  222. package/skills/lit-typographic-motion/engine/stage/cover-init.js +12 -1
  223. package/skills/lit-typographic-motion/engine/stage/frame-worker.mjs +15 -7
  224. package/skills/lit-typographic-motion/engine/stage/gate.mjs +48 -4
  225. package/skills/lit-typographic-motion/engine/stage/qa-page.js +16 -5
  226. package/skills/lit-typographic-motion/engine/stage/qa.mjs +148 -7
  227. package/skills/lit-typographic-motion/engine/stage/render.mjs +69 -7
  228. package/skills/lit-typographic-motion/engine/stage/scan.mjs +20 -1
  229. package/skills/lit-typographic-motion/engine/stage/stage-kit.js +85 -4
  230. package/skills/lit-typographic-motion/engine/stills.mjs +1 -1
  231. package/skills/lit-typographic-motion/engine/styles.mjs +221 -0
  232. package/skills/lit-typographic-motion/engine/text.mjs +1 -1
  233. package/skills/lit-typographic-motion/engine/treatment.mjs +190 -6
  234. package/skills/lit-typographic-motion/engine/vendor/three/LICENSE +21 -0
  235. package/skills/lit-typographic-motion/engine/vendor/three/addons/environments/RoomEnvironment.js +144 -0
  236. package/skills/lit-typographic-motion/engine/vendor/three/addons/geometries/RoundedBoxGeometry.js +155 -0
  237. package/skills/lit-typographic-motion/engine/vendor/three/three.module.js +54571 -0
  238. package/skills/lit-typographic-motion/gate.mjs +18 -2
  239. package/skills/lit-typographic-motion/probe.mjs +13 -1
  240. package/skills/lit-typographic-motion/recipes/hero-3d/expected.json +64 -0
  241. package/skills/lit-typographic-motion/recipes/hero-3d/index.html +132 -0
  242. package/skills/lit-typographic-motion/recipes/orbit-loop/expected.json +2 -0
  243. package/skills/lit-typographic-motion/recipes/painted-light/expected.json +2 -0
  244. package/skills/lit-typographic-motion/recipes/weight-tide/expected.json +2 -0
  245. package/skills/lit-typographic-motion/references/cover.md +56 -13
  246. package/skills/lit-typographic-motion/references/craft-loop.md +20 -7
  247. package/skills/lit-typographic-motion/references/depth.md +112 -0
  248. package/skills/lit-typographic-motion/references/runtime.md +21 -6
  249. package/skills/lit-typographic-motion/references/sound.md +76 -0
  250. package/skills/lit-typographic-motion/references/stage.md +50 -7
  251. package/skills/lit-typographic-motion/references/studio.md +162 -0
  252. package/skills/lit-typographic-motion/references/style-bibles.md +18 -0
  253. package/skills/lit-typographic-motion/references/treatment.md +108 -9
  254. package/skills/lit-typographic-motion/render.mjs +60 -3
  255. package/skills/lit-typographic-motion/requirements-audio.txt +3 -3
  256. package/skills/lit-typographic-motion/styles/paper.json +185 -0
  257. package/skills/lit-typographic-motion/styles/swiss-signal.json +188 -0
  258. package/skills/lit-typographic-motion/styles/terminalcore.json +182 -0
  259. package/skills/lit-typographic-motion/styles/tidal.json +187 -0
  260. package/skills/litresearch/SKILL.md +2 -0
  261. package/skills/managed-skill-manifest.json +520 -91
  262. package/skills/readme-studio/SKILL.md +1 -1
  263. package/skills/readme-studio/templates/hyperframes/package-lock.json +3 -3
  264. package/skills/readme-studio/templates/remotion/package-lock.json +3 -3
  265. package/skills/review-work/SKILL.md +2 -0
  266. package/skills/start-work/SKILL.md +8 -0
  267. package/skills/workflow-loop/SKILL.md +4 -0
  268. package/tools/check-pack-payload.mjs +4 -2
  269. package/tools/payload-substance-parity.json +4 -3
  270. package/tools/qa-cover-recipes.mjs +4 -3
@@ -0,0 +1,226 @@
1
+ // frontend-ui-ux kit: behaviour that every build otherwise re-invents. No dependencies, no
2
+ // network. Colours come from the locked tokens (CSS custom properties), never from literals.
3
+ // UI.bars(el, { labels, series: [{ name, values, role }], marker, unit, caption })
4
+ // UI.lines(el, { labels, series: [{ name, values, role }], marker, unit, caption })
5
+ // <table data-ui-sortable> header <button class="ui-sort"> cells sort by data-value or text
6
+ // <div role="tablist" data-ui-tabs> arrow keys, Home and End move between tabs
7
+ (function () {
8
+ "use strict";
9
+ const NS = "http://www.w3.org/2000/svg";
10
+ const svg = (tag, attrs = {}) => {
11
+ const node = document.createElementNS(NS, tag);
12
+ for (const [key, value] of Object.entries(attrs)) node.setAttribute(key, String(value));
13
+ return node;
14
+ };
15
+ const roleColor = (role, index) => `var(--c-${role || `chart-${(index % 5) + 1}`})`;
16
+ const niceMax = (value) => {
17
+ if (value <= 0) return 1;
18
+ const power = 10 ** Math.floor(Math.log10(value));
19
+ const step = [1, 2, 2.5, 5, 10].find((n) => n * power >= value / 4) * power;
20
+ return Math.ceil(value / step) * step;
21
+ };
22
+ const fmt = (value, unit) => `${Number(value).toLocaleString()}${unit ?? ""}`;
23
+
24
+ function frame(el, opts, maxValue) {
25
+ el.classList.add("ui-chart");
26
+ el.replaceChildren();
27
+ const width = Math.max(280, Math.round(el.getBoundingClientRect().width) || 640);
28
+ const height = opts.height ?? (width < 480 ? 200 : 240);
29
+ const pad = { top: 16, right: 8, bottom: 28, left: 36 };
30
+ const plotW = width - pad.left - pad.right;
31
+ const plotH = height - pad.top - pad.bottom;
32
+ const root = svg("svg", { viewBox: `0 0 ${width} ${height}`, width, height, role: "img", "aria-label": opts.caption ?? "Chart" });
33
+ // Whole-number data keeps whole-number ticks: round the top up to a multiple of the four steps.
34
+ const top = opts.integer ? Math.max(4, Math.ceil(niceMax(maxValue) / 4) * 4) : niceMax(maxValue);
35
+ const axis = svg("g", { class: "ui-axis" });
36
+ for (let i = 0; i <= 4; i += 1) {
37
+ const y = pad.top + plotH - (plotH * i) / 4;
38
+ root.append(svg("line", { class: i === 0 ? "ui-baseline" : "ui-grid-line", x1: pad.left, x2: width - pad.right, y1: y, y2: y }));
39
+ const label = svg("text", { x: pad.left - 8, y: y + 4, "text-anchor": "end" });
40
+ label.textContent = fmt((top * i) / 4, "");
41
+ axis.append(label);
42
+ }
43
+ root.append(axis);
44
+ return { root, axis, width, height, pad, plotW, plotH, top };
45
+ }
46
+
47
+ function legendAndTable(el, opts) {
48
+ if (opts.series.length > 1) {
49
+ const legend = document.createElement("div");
50
+ legend.className = "ui-legend";
51
+ opts.series.forEach((series, index) => {
52
+ const item = document.createElement("span");
53
+ const swatch = document.createElement("i");
54
+ swatch.style.setProperty("--swatch", roleColor(series.role, index));
55
+ item.append(swatch, document.createTextNode(series.name));
56
+ legend.append(item);
57
+ });
58
+ el.prepend(legend);
59
+ }
60
+ const details = document.createElement("details");
61
+ const summary = document.createElement("summary");
62
+ summary.className = "ui-meta";
63
+ summary.textContent = opts.tableLabel ?? "Show as table";
64
+ const table = document.createElement("table");
65
+ table.className = "ui-table ui-table--compact";
66
+ const head = document.createElement("tr");
67
+ for (const text of [opts.indexLabel ?? "", ...opts.series.map((s) => s.name)]) {
68
+ const th = document.createElement("th");
69
+ th.textContent = text;
70
+ head.append(th);
71
+ }
72
+ const thead = document.createElement("thead");
73
+ thead.append(head);
74
+ const tbody = document.createElement("tbody");
75
+ opts.labels.forEach((label, row) => {
76
+ const tr = document.createElement("tr");
77
+ const th = document.createElement("th");
78
+ th.scope = "row";
79
+ th.textContent = label;
80
+ tr.append(th);
81
+ for (const series of opts.series) {
82
+ const td = document.createElement("td");
83
+ td.className = "is-num";
84
+ td.textContent = fmt(series.values[row], opts.unit);
85
+ tr.append(td);
86
+ }
87
+ tbody.append(tr);
88
+ });
89
+ table.append(thead, tbody);
90
+ details.append(summary, table);
91
+ el.append(details);
92
+ }
93
+
94
+ function xLabels(f, labels, step) {
95
+ const every = Math.max(1, Math.ceil(labels.length / (f.plotW / 44)));
96
+ labels.forEach((label, index) => {
97
+ if (index % every) return;
98
+ const text = svg("text", { x: f.pad.left + step * index + (step / 2) * (f.bandCenter ? 1 : 0), y: f.height - 8, "text-anchor": "middle" });
99
+ text.textContent = label;
100
+ f.axis.append(text);
101
+ });
102
+ }
103
+
104
+ function marker(f, opts, x) {
105
+ if (!opts.marker) return;
106
+ f.root.append(svg("line", { class: "ui-marker", x1: x, x2: x, y1: f.pad.top - 4, y2: f.pad.top + f.plotH }));
107
+ const text = svg("text", { class: "ui-marker-label", x: x + 4, y: f.pad.top + 6 });
108
+ text.textContent = opts.marker.label;
109
+ f.root.append(text);
110
+ }
111
+
112
+ const drawn = new Map();
113
+ let resizeTimer = 0;
114
+ addEventListener("resize", () => {
115
+ clearTimeout(resizeTimer);
116
+ resizeTimer = setTimeout(() => { for (const [el, draw] of drawn) draw(); }, 150);
117
+ });
118
+ const remember = (el, draw) => { drawn.set(el, draw); draw(); };
119
+
120
+ function bars(el, opts) { remember(el, () => drawBars(el, opts)); }
121
+ function lines(el, opts) { remember(el, () => drawLines(el, opts)); }
122
+
123
+ function drawBars(el, opts) {
124
+ const stacked = Boolean(opts.stacked);
125
+ const totals = opts.labels.map((_, i) => opts.series.reduce((sum, s) => sum + (stacked ? s.values[i] : 0), 0));
126
+ const max = stacked ? Math.max(...totals) : Math.max(...opts.series.flatMap((s) => s.values));
127
+ const f = frame(el, { ...opts, integer: opts.series.every((s) => s.values.every(Number.isInteger)) }, max);
128
+ f.bandCenter = true;
129
+ const step = f.plotW / opts.labels.length;
130
+ const groupW = step * 0.64;
131
+ const barW = stacked ? groupW : groupW / opts.series.length;
132
+ opts.labels.forEach((label, i) => {
133
+ let base = 0;
134
+ opts.series.forEach((series, s) => {
135
+ const value = series.values[i];
136
+ const h = (value / f.top) * f.plotH;
137
+ const x = f.pad.left + step * i + (step - groupW) / 2 + (stacked ? 0 : barW * s);
138
+ const y = f.pad.top + f.plotH - h - (stacked ? (base / f.top) * f.plotH : 0);
139
+ const rect = svg("rect", { x, y, width: Math.max(1, barW - (stacked ? 0 : 2)), height: Math.max(0, h), rx: 2, style: `fill: ${roleColor(series.role, s)}` });
140
+ const title = svg("title");
141
+ title.textContent = `${label}: ${series.name} ${fmt(value, opts.unit)}`;
142
+ rect.append(title);
143
+ f.root.append(rect);
144
+ base += stacked ? value : 0;
145
+ });
146
+ });
147
+ xLabels(f, opts.labels, step);
148
+ if (opts.marker) marker(f, opts, f.pad.left + step * opts.marker.index + step / 2);
149
+ el.append(f.root);
150
+ legendAndTable(el, opts);
151
+ }
152
+
153
+ function drawLines(el, opts) {
154
+ const max = Math.max(...opts.series.flatMap((s) => s.values));
155
+ const f = frame(el, { ...opts, integer: opts.series.every((s) => s.values.every(Number.isInteger)) }, max);
156
+ const step = f.plotW / Math.max(1, opts.labels.length - 1);
157
+ opts.series.forEach((series, s) => {
158
+ const points = series.values.map((value, i) => `${f.pad.left + step * i},${f.pad.top + f.plotH - (value / f.top) * f.plotH}`);
159
+ f.root.append(svg("polyline", { points: points.join(" "), style: `fill: none; stroke: ${roleColor(series.role, s)}`, "stroke-width": 2, "stroke-linejoin": "round", "stroke-linecap": "round" }));
160
+ });
161
+ f.bandCenter = false;
162
+ xLabels(f, opts.labels, step);
163
+ if (opts.marker) marker(f, opts, f.pad.left + step * opts.marker.index);
164
+ el.append(f.root);
165
+ legendAndTable(el, opts);
166
+ }
167
+
168
+ function sortable(table) {
169
+ const tbody = table.tBodies[0];
170
+ table.querySelectorAll("thead th .ui-sort").forEach((button) => {
171
+ button.type = "button";
172
+ button.addEventListener("click", () => {
173
+ const th = button.closest("th");
174
+ const index = [...th.parentElement.children].indexOf(th);
175
+ const next = th.getAttribute("aria-sort") === "ascending" ? "descending" : "ascending";
176
+ table.querySelectorAll("thead th[aria-sort]").forEach((cell) => cell.removeAttribute("aria-sort"));
177
+ th.setAttribute("aria-sort", next);
178
+ const key = (row) => {
179
+ const cell = row.children[index];
180
+ const raw = cell?.dataset.value ?? cell?.textContent.trim() ?? "";
181
+ const number = Number(raw);
182
+ return raw !== "" && !Number.isNaN(number) ? number : raw.toLocaleLowerCase();
183
+ };
184
+ const rows = [...tbody.rows].sort((a, b) => {
185
+ const x = key(a);
186
+ const y = key(b);
187
+ const order = typeof x === "number" && typeof y === "number" ? x - y : String(x).localeCompare(String(y));
188
+ return next === "ascending" ? order : -order;
189
+ });
190
+ tbody.append(...rows);
191
+ table.dispatchEvent(new CustomEvent("ui:sorted", { detail: { index, order: next } }));
192
+ });
193
+ });
194
+ }
195
+
196
+ function tabs(list) {
197
+ const items = [...list.querySelectorAll('[role="tab"]')];
198
+ const select = (tab) => {
199
+ for (const item of items) {
200
+ const on = item === tab;
201
+ item.setAttribute("aria-selected", String(on));
202
+ item.tabIndex = on ? 0 : -1;
203
+ const panel = document.getElementById(item.getAttribute("aria-controls"));
204
+ if (panel) panel.hidden = !on;
205
+ }
206
+ };
207
+ items.forEach((tab, index) => {
208
+ tab.addEventListener("click", () => select(tab));
209
+ tab.addEventListener("keydown", (event) => {
210
+ const move = { ArrowRight: 1, ArrowLeft: -1, Home: -index, End: items.length - 1 - index }[event.key];
211
+ if (move === undefined) return;
212
+ event.preventDefault();
213
+ const next = items[(index + move + items.length) % items.length];
214
+ next.focus();
215
+ select(next);
216
+ });
217
+ });
218
+ select(items.find((tab) => tab.getAttribute("aria-selected") === "true") ?? items[0]);
219
+ }
220
+
221
+ document.addEventListener("DOMContentLoaded", () => {
222
+ document.querySelectorAll("table[data-ui-sortable]").forEach(sortable);
223
+ document.querySelectorAll("[data-ui-tabs]").forEach(tabs);
224
+ });
225
+ window.UI = { bars, lines, sortable, tabs };
226
+ })();
@@ -13,7 +13,7 @@ Use only what the user owns or supplied; everything else is unlicensed until pro
13
13
  Say so, then pick a neutral system a brand can replace in one token pass.
14
14
 
15
15
  - Build on semantic tokens only, so a later palette swap touches no component.
16
- - One neutral ramp, one accent, no logotype; set the product name in the body face as the wordmark.
16
+ - One neutral ramp, one accent role and a second accent role that the lock's tokens name (`--accent`, `--accent-2`), no logotype; set the product name in the body face as the wordmark.
17
17
  - Record an omission id naming what is missing: mark, palette, typeface, imagery, voice.
18
18
  - Never invent a logo, tagline, or brand story and present it as the user's.
19
19
 
@@ -23,7 +23,7 @@ Convert each adjective into three or more numeric commitments, or drop it.
23
23
 
24
24
  - Precise: tabular figures, 4 px grid, 1 px hairline borders, 120 ms transitions.
25
25
  - Warm: radii 12 px or greater, body line-height 1.6, photography over iconography.
26
- - Serious: one accent hue, elevation capped at two levels, motion under 200 ms.
26
+ - Serious: one accent hue (a calm lock), elevation capped at two levels, motion under 200 ms.
27
27
 
28
28
  ## Specifying a generated reference image
29
29
 
@@ -151,7 +151,7 @@ Useful evidence is falsifiable: an exact file hash, an exact command, an explici
151
151
 
152
152
  For corpus verification, resolve the helper from the selected installed skill, never from the project cwd: set `FRONTEND_SKILL_FILE` to its absolute `SKILL.md` path, set `FRONTEND_SKILL_ROOT` to the physical directory containing that file, then run `node "$FRONTEND_SKILL_ROOT/scripts/verify-canonical-corpus.mjs" --json`. Beta2 and `litfamily.design-contract/v1beta1` remain evidence-eligible; alpha returns `LEGACY_SCHEMA_V1ALPHA1`. Keep the dataset separate from the canonical library and imported scripts inert.
153
153
 
154
- Eighteen authored reference documents ship inside this skill under `skills/frontend-ui-ux/references/`. Each is hash-pinned in the managed manifest and installed with the skill. Open by question, not by curiosity: a reference nobody can find is a reference that does not exist.
154
+ Nineteen authored reference documents ship inside this skill under `skills/frontend-ui-ux/references/`. Each is hash-pinned in the managed manifest and installed with the skill. Open by question, not by curiosity: a reference nobody can find is a reference that does not exist.
155
155
 
156
156
  | Reference | Question it answers |
157
157
  | --- | --- |
@@ -173,6 +173,7 @@ Eighteen authored reference documents ship inside this skill under `skills/front
173
173
  | [evidence-review.md](references/evidence-review.md) | How is the independent review pass coupled through the contract hash and evidence schema rather than shared assumptions? |
174
174
  | [craft-floor.md](references/craft-floor.md) | Which CF and RS values should the live probe measure, and what remains review-only? |
175
175
  | [slop-register.md](references/slop-register.md) | Which numbered visual/functional signatures does the probe or reviewer need to inspect? |
176
+ | [house-styles.md](references/house-styles.md) | Which house style, layout family and signature moment does a new build lock, which kit classes may it use, and which DS rules and LOOK questions does the probe apply? |
176
177
 
177
178
  Supporting shipped assets: [design-contract-v1beta2.json](schemas/design-contract-v1beta2.json) is the canonical evidence-eligible shape; [design-contract-v1beta1.json](schemas/design-contract-v1beta1.json) remains the compatible evidence-eligible shape; [design-contract-v1alpha1.json](schemas/design-contract-v1alpha1.json) is retained for migration diagnostics. `scripts/uiux.mjs` provides the validator and retrieval entry point, `scripts/design-contract.mjs` owns executable rules, and `data/` contains the packaged offline records with their license, provenance, and notice.
178
179
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  This is the authored rule sheet for the interface probe. `scripts/probe-thresholds.json` is the executable value register. Keep a changed number in that one file, then update this explanation and its fixture; never adjust a value after reading an A/B judge result. DET means the browser can inspect a signal. JUDG needs a reviewer, even when the browser offers a count. The probe records `not_verified` for a signal it cannot observe. A clean source search is never a rendered PASS.
4
4
 
5
+ ## Design lock rules
6
+
7
+ When `design.lock.json` sits beside the page (or `--lock` names one), the probe also runs the DS-* lock rules, listed with their severities in `references/house-styles.md` and registered in `scripts/probe-lock.mjs`, records the lock under `manifest.design_lock`, and prints LOOK-1 to LOOK-6 with the 1440, 390 and 320 screenshots. Answer the LOOK questions by eye, fix the two worst answers and probe again; two look rounds at most. Without a lock the probe records DS-001 in `not_verified`.
8
+
5
9
  ## Typography
6
10
 
7
11
  | Rule | Working decision |
@@ -24,7 +28,7 @@ This is the authored rule sheet for the interface probe. `scripts/probe-threshol
24
28
  | CF-202 | Focused and unfocused styles must differ. Native `outline-style: auto` passes. A custom ring needs 2px and 3:1; an inadequate indicator is MEDIUM, no change is HIGH. The probe focuses a control without clicking or pressing it. |
25
29
  | CF-203 | APCA is advisory alongside the WCAG pair: body Lc 75/90, other text 60/75, large text 45/60, UI 30. It cannot block alone. |
26
30
  | CF-204 | Sample imagery or gradients behind glyphs when same-origin pixels are available. Cross-origin paint remains `not_verified`. |
27
- | CF-205 | At most one high-saturation interactive accent cluster per view. Count filled emphasis surfaces of at least 24×24px, not every coloured border. Review decides whether separate status hues are justified. |
31
+ | CF-205 | At most four high-saturation hue clusters per view (the rich default: an accent, a second accent and status hues); one when the lock records calm density. Count filled emphasis surfaces of at least 24×24px, not every coloured border. Review decides whether separate status hues are justified. |
28
32
  | CF-206 | Hues within 15 degrees serve one semantic colour. |
29
33
  | CF-207 | Status has text, icon or pattern as well as colour; absence of a second channel is HIGH. |
30
34
  | CF-301 | Spacing values at least 4px sit within 1px of a 4px multiple. One to three distinct off-scale values are LOW; more than three are MEDIUM. Skip auto margins. |
@@ -41,7 +45,7 @@ This is the authored rule sheet for the interface probe. `scripts/probe-threshol
41
45
  | CF-401 | For a rounded child that traces its parent's content box, compare each corner with outer radius minus padding; allow the rule register's tolerance. Skip icon-sized children and heavily padded parents. |
42
46
  | CF-402 | A border plus a near-zero-blur edge shadow redraws the same edge. A soft ambient shadow is acceptable. |
43
47
  | CF-403 | A hard offset shadow needs a recorded visual register; otherwise flag it for review. |
44
- | CF-404 | A primary action should not carry a chromatic, large-blur halo. Parse blur, colour and offset rather than banning all shadows. |
48
+ | CF-404 | A primary action should not carry a chromatic, large-blur halo. A glow on a decorative `ui-glow` surface is not a control halo; three or more recurring glows fall under SLOP-010. Parse blur, colour and offset rather than banning all shadows. |
45
49
  | CF-405 | Photo outlines should be thin and low saturation, not a loud accent border. |
46
50
  | CF-406 | Modal scrims use a solid or near-solid veil, not backdrop blur by reflex. |
47
51
  | CF-407 | Grids, radial halos, marquees and stripe pseudo-elements are reviewer cues unless tied to content. |
@@ -3,6 +3,9 @@
3
3
  "origin": "authored defaults; not measurements of an external design",
4
4
  "spacing": {"base": 8, "micro": 4, "sectionDesktop": [80,128], "sectionMobile": [40,64]},
5
5
  "type": {"displayDesktop": [56,104], "displayMobile": [36,56], "latinLeading": [0.95,1.05], "koreanLeading": [1.12,1.25], "bodyLeading": [1.5,1.7]},
6
+ "density": {"default": "rich", "calm_only_when": "the request asks for a minimal, calm, quiet or restrained page, or scripts/design-lock.mjs init --density calm; calm restores 64 to 128 px sections, a flat canvas and no glow"},
7
+ "palette": {"paper": "#f5f3eb", "ink": "#18272b", "accent": "#c64d2e", "accent_2": "#1d6b68", "mist": "#b7d8c7"},
8
+ "work_surfaces": {"applies_to": "screens people work in repeatedly (admin consoles, analytics views, settings, internal tools) rather than pages read once", "first_viewport": "the working content and primary controls; no hero headline, tagline or illustration block above them", "pixel_art": "a small accent at most, never the primary visual anchor", "lock": "these screens lock the graphite house style, not this palette"},
6
9
  "composition": ["deliberate line breaks", "compact display leading", "terminal micro-labels", "thin boundaries", "atmospheric fields", "intentional asymmetry"],
7
10
  "pixel_illustration": {
8
11
  "default_when_brief_unspecified": true,
@@ -0,0 +1,236 @@
1
+ # House styles, the kit and the design lock
2
+
3
+ Read this before writing markup for a new page or screen. A script writes the look into the project
4
+ (typeface, palette, scale, radii, component anatomy, page skeleton) from a small closed set, and the
5
+ interface probe checks the rendered page against it, so the same brief lands in the same register
6
+ whichever run builds it. Content, composition inside the skeleton and accent art stay open. Every
7
+ page also gets one place where it may be bold, its signature moment (section 6), declared in the
8
+ lock and checked like the rest.
9
+
10
+ ## 1. Lock the look first
11
+
12
+ Run every command from the directory OpenCode's native skill tool returned for this skill (the one
13
+ holding `SKILL.md`), with an absolute `--dir`.
14
+
15
+ ```bash
16
+ node scripts/design-lock.mjs list
17
+ node scripts/design-lock.mjs match "<the request>"
18
+ node scripts/design-lock.mjs init --family <family> [--style <id>] [--brief "<the request>"] [--density calm] --dir <project> --scaffold \
19
+ --moment <kind> --moment-note "<what it shows>" [--moment-place "<where it sits>"]
20
+ node scripts/design-lock.mjs moment --dir <project> --kind <kind> --note "<what it shows>"
21
+ ```
22
+
23
+ `init` writes into the project:
24
+
25
+ | File | What it is |
26
+ |---|---|
27
+ | `design.lock.json` | The declaration the probe reads: style id, family, density, fonts, type scale, display range and moment ceiling, radii, spacing, light and dark palette, elevation, and the signature moment (scale, kind, note, place) |
28
+ | `ui/tokens.css` | Generated custom properties (`--c-*`, `--fs-*`, `--fw-*`, `--sp-*`, `--radius-*`, `--shadow-*`, `--dur-*`) with dark mode and the `@font-face` rules |
29
+ | `ui/components.css` | The shared `ui-*` component sheet; it reads only tokens |
30
+ | `ui/ui.js` | Charts with a table fallback, sortable tables, keyboard tabs |
31
+ | `ui/fonts/` | Pretendard (Latin and Hangul) and JetBrains Mono with their OFL texts, copied from the sibling skills `lit-diagram-drawer` and `lit-typographic-motion` after a sha256 check (`kit/fonts.json`); nothing is fetched. Pretendard is the unmodified official file, never subset |
32
+ | `index.html` | Only with `--scaffold` and no existing page: the starter for the family |
33
+
34
+ **Density.** Every lock records `density: rich` unless the request asks for a calm page. Rich is
35
+ the default because the user wants pages that feel made, not emptied: a tighter section rhythm
36
+ (48, 72 and 96 px), the style's texture (field-notes notebook dots), a second accent family
37
+ (`--c-accent-2`, `-ink`, `-wash`, `on-accent-2`), colour bands (`ui-band`, `ui-band--accent-2`,
38
+ `ui-band--support`), a two-accent wash (`ui-wash`) and an atmosphere glow (`ui-glow`, a soft
39
+ glow token that is `none` on flat and offset locks). `density: calm` is opt-in: `--density calm`,
40
+ or a brief that says minimal, calm, quiet, restrained, understated, 미니멀, 차분, 절제, 담백 or 간결
41
+ (`match` prints the density it reads). Calm restores the broad 64, 96 and 128 px sections, a flat
42
+ canvas, no glow, and holds the probe to one accent hue (CF-205). Contrast, focus, overflow,
43
+ reduced motion and every other floor are the same in both.
44
+
45
+ Choose in this order and stop at the first rule that decides:
46
+
47
+ 1. **Existing product or brand tokens** (brownfield, a design system in the repo, colours or type the
48
+ user named): do not lock a kit style over them. Either write a style file in the same schema as
49
+ `kit/styles/*.json` with the brand's values and lock it with `--from <file>` (a new id), or skip
50
+ the lock and say the DS rules did not run.
51
+ 2. **The brief names a register.** A style keyword in the request picks it: pass the request as
52
+ `--brief` (or run `match`) and `init` locks the style whose keyword it contains, the longest
53
+ keyword winning, so "neo-brutalism" never lands on `computational-brutalism`. Brutalist,
54
+ brutalism, retro-futurist, CRT, 브루탈리즘 and 레트로퓨처 pick `computational-brutalism`;
55
+ neo-brutalism, neubrutalism and 네오브루탈리즘 pick `neo-brutalism`; `list` prints every
56
+ keyword. A register named in other words matches a style's `use_when` line: lock that style.
57
+ 3. **Competing directions left open** by the brief: ask, as `references/production.md`
58
+ says, before locking.
59
+ 4. **No visual direction at all:** the family decides, and `init` without `--style` applies it.
60
+ A `landing` page (read once: landing, docs, report) locks `field-notes`, the authored default
61
+ profile (`references/default-editorial-pixel.json`) made complete: same palette, spacing, type
62
+ ranges and pixel-art rule, plus the fonts, radii, elevation and component anatomy the profile
63
+ left open. A `workspace` or `settings` screen (dashboards, settings, forms, admin and data-dense
64
+ screens) locks `graphite`. `meadow` is never a default; it needs a brief that asks for its
65
+ register. The same holds for `computational-brutalism` and `neo-brutalism`: opt-in only.
66
+
67
+ | Style | Register | Accent art |
68
+ |---|---|---|
69
+ | `field-notes` | Default for `landing`. Warm paper with notebook dots, deep ink, a terracotta accent with a deep-teal second accent, mist as the tint, thin rules instead of shadows, large editorial title, small lowercase mono labels | An original pixel motif on read-once pages (section 5) |
70
+ | `graphite` | Default for `workspace` and `settings`. Cool greys, white surfaces, a blue accent with a rose second accent, 6–8 px radii, soft two-layer elevation | None |
71
+ | `meadow` | Opt-in only. Near-white, deep teal accent, coral second accent, sand tint, 12–18 px radii, soft elevation | None |
72
+ | `computational-brutalism` | Opt-in only. Retro-futurist instrument with its structure on show: drafting-film grey on a 32 px plotting grid with a stronger line every 160 px, monospaced text and mono headings and figures at real size, square corners, 3 px section rules carrying `data-coord` labels, 2 px compartments with inverse ink header bars, uppercase mono meta, heavy uppercase display, a hazard-orange signal with a plotter-blue second accent; dark mode is a phosphor CRT with an amber signal. Flat elevation | Instrument parts (section 5) |
73
+ | `neo-brutalism` | Opt-in only. Lavender canvas, white surfaces, 3 px ink outlines on surfaces and 2 px on controls, zero-blur ink offset shadows (6 px surfaces, 3 px controls), solid sunflower-yellow panel heads and accent with ink text, an electric-blue second accent, pink support block, chunky 900-weight display and 800-weight headings, buttons that press into their shadow. Offset elevation | Stickers and colour blocks (section 5) |
74
+
75
+ Each style file also carries `signature` (what must show for the register to read) and `forbid`
76
+ (what breaks it); `design-lock.mjs list` prints both. The two brutalist registers differ on
77
+ purpose: computational brutalism is flat, square and monospaced, neo-brutalism is outlined, offset
78
+ and loud. Never mix their signatures on one page.
79
+
80
+ Families decide the skeleton, not the look:
81
+
82
+ | Family | Use for | Starter |
83
+ |---|---|---|
84
+ | `landing` | Pages read once: landing, product, launch, portfolio, docs home | Site bar, split hero, steps, feature split with a real kit view, quote, three plans, FAQ, footer |
85
+ | `workspace` | Screens worked in: dashboards, consoles, admin, tables | App shell (side nav, top bar), headline figures, 8 + 4 panel row, full-width table with filter and search |
86
+ | `settings` | Guided forms: settings, account, onboarding, checkout | Section index plus one column capped near 720 px, labelled fields with hints and errors, save bar with status, danger zone with a typed confirmation dialog |
87
+
88
+ A starter is a skeleton: keep its order unless the brief says otherwise, delete what the brief does
89
+ not ask for, add sections by reusing its parts. Replace every `{{slot}}`; the probe fails a page
90
+ that still shows one (DS-006). Mixed products use one style and the family of each screen.
91
+
92
+ ## 2. Build only with the kit
93
+
94
+ - Link `ui/tokens.css` first, then `ui/components.css`, then page rules. Load `ui/ui.js` with
95
+ `defer` when a chart, sortable table or tab set is on the page.
96
+ - Page rules may set layout (grid areas, widths, order, `gap`) and may only use `var(--…)` for
97
+ colour, type size, weight, radius, shadow, spacing and duration. A literal colour, font family or
98
+ off-scale size is drift; the probe reports it (DS-002 to DS-005).
99
+ - Something the kit lacks is built from tokens in a page rule, in the kit's idiom. It is not a reason
100
+ to import a framework, a second icon set or another font.
101
+ - Copy is still the work: product names, numbers and sentences specific to the brief. Mark invented
102
+ figures as examples where a reader could take them as real.
103
+
104
+ ### Class catalogue
105
+
106
+ | Area | Classes |
107
+ |---|---|
108
+ | Type | `ui-display` (the one display title), `ui-h1` `ui-h2` `ui-h3` `ui-h4`, `ui-lead`, `ui-body`, `ui-muted`, `ui-small`, `ui-meta`, `ui-label` (mono, lowercase, short), `ui-num` (tabular), `ui-code` |
109
+ | Layout | `ui-container` (`--narrow`, `--wide`), `ui-section` (`--sunken`), `ui-band` (`--accent-2`, `--support`: a full-bleed colour band whose text takes the band's ink), `ui-section-head`, `ui-stack` (`--tight`, `--loose`), `ui-cluster`, `ui-split` (`--even`, `--top`), `ui-grid` with `--cols` |
110
+ | Actions | `ui-btn` with `--primary`, `--ink`, `--quiet`, `--danger`, `--sm`; one primary per view |
111
+ | Forms | `ui-field` with `__label`, `__hint`, `__error`; `ui-input`, `ui-select`, `ui-textarea`; `ui-input-group` with `__addon`; `ui-check`; `ui-switch` (a checkbox with `role="switch"`); `ui-segmented` (buttons with `aria-pressed`); `ui-form-actions` |
112
+ | Surfaces | `ui-panel` (`--flush`, `--accent-2`) with `__head` and `__title`; `ui-wash` (the two-accent wash); `ui-glow` (the lock's glow, for one surface such as the moment); `ui-rule`; `ui-callout` (`--info`, `--positive`, `--warning`, `--negative`) |
113
+ | Status | `ui-status` (`--positive`, `--warning`, `--negative`, `--info`) always with words; `ui-tag` with the same variants, `--accent` and `--accent-2` |
114
+ | Figures | `ui-stat-row` of `ui-stat` with `__label`, `__value` (a `div`), `__note`; `ui-meter` with an inner `span` width |
115
+ | Tables | `ui-table-wrap` around every `ui-table` (`--compact`, `--stack` for phones with `data-label` on each cell); numeric cells `is-num`; sortable headers `<th><button class="ui-sort">` inside `table[data-ui-sortable]` |
116
+ | Lists | `ui-list`, `ui-steps` (an `ol` of real steps), `ui-checklist` |
117
+ | Disclosure | `details.ui-disclosure`, `ui-tabs` with `role="tablist"` and `data-ui-tabs`, `dialog.ui-dialog` with `__body` and `__actions`, `ui-toast-region` and `ui-toast` |
118
+ | Site | `ui-skip`, `ui-sitebar` with `__inner`, `ui-brand`, `ui-sitenav`, `details.ui-menu` with `__panel`, `ui-hero` (`--stage`: the art takes the larger column and the height) with `__copy` and `__art`, `ui-quote`, `ui-plans` of `ui-plan` (`--featured`) with `__price`, `ui-cta-band`, `ui-footer`, `ui-pixel` |
119
+ | App | `ui-app` with `__nav` and `__main`, `ui-navgroup` with `__label`, `ui-navlist` with `aria-current`, `ui-topbar`, `ui-page` with `__head` and `__title`, `ui-workgrid` with `ui-span-12/8/7/6/5/4/3`, `ui-scroll`, `ui-toolbar`, `ui-search`, `ui-empty` |
120
+ | Settings | `ui-settings` with `__nav` and `__body`, `ui-setting-section` (`--danger`) with `__head`, `ui-savebar` |
121
+ | Moment | `ui-setpiece` (display type at the lock's moment size), `ui-bignum` (one figure at moment size, heading font, tabular); both only inside `[data-ui-moment]` (section 6) |
122
+ | Signature | `ui-coord` (bracketed mono coordinate or measurement label), `ui-prompt` (a `>` prefix; `--cursor` adds a static block cursor), `data-coord="…"` on a `ui-section` or `ui-hero` (a label on the section rule where the lock shows section marks), `ui-ruler` (tick bar), `ui-frame` (a ruled box with registration crosses), `ui-scan` (static scanlines), `ui-readout` (mono value well), `ui-panel--accent` and `ui-panel--support` (colour blocks), `ui-sticker` (tilted tag) |
123
+ | Charts | `UI.bars(el, { labels, series: [{ name, values, role: "chart-1" }], marker: { index, label }, caption, indexLabel, unit, stacked })` and `UI.lines(…)`; roles are `chart-1` to `chart-5`; each chart gets a legend when it has two series and a "Show as table" fallback |
124
+
125
+ ## 3. What the probe checks against the lock
126
+
127
+ `scripts/probe.mjs` finds `design.lock.json` beside the page it serves (or takes `--lock <path>`)
128
+ and runs these rules at every matrix row. Without a lock they are listed under Not verified.
129
+
130
+ | Id | Rule | Severity |
131
+ |---|---|---|
132
+ | DS-001 | `ui/tokens.css` is not applied: `--ui-lock` on `:root` is missing or names another style | HIGH |
133
+ | DS-002 | Visible text set in a family the lock does not name | HIGH |
134
+ | DS-003 | Text, background, border or SVG colour more than 8 per channel from every palette role (light and dark); four or more distinct colours is HIGH, fewer is MEDIUM | HIGH / MEDIUM |
135
+ | DS-004 | Font size neither on the scale (±0.5 px) nor inside the display range | MEDIUM |
136
+ | DS-005 | Corner radius not in the lock and not fully round | LOW |
137
+ | DS-006 | A `{{slot}}` from a starter left in text, a title or a labelling attribute (the static fallback reads `.html` files only, since framework templates use braces) | HIGH |
138
+ | DS-007 | A shadow outside the lock's elevation grammar: a `flat` lock (computational-brutalism) takes only rings, no blur and no offset past a hairline; an `offset` lock (neo-brutalism) takes zero-blur offsets but no blur. Inset shadows never count; a `soft` lock is not shadow-checked by DS-007 | MEDIUM |
139
+ | DS-008 | The signature moment: the lock declares none (a lock written before signature moments counts; add one with `design-lock.mjs moment`); no visible `[data-ui-moment]`; the marked kind differs from the lock; it is smaller than 40 000 px² (bold) or 4 800 px² (small); a read-once page's moment starts below the first screen at 1024 px and wider (more than 120 px short of the fold) or past 1.5 screens on narrower viewports; it lacks what its kind needs (section 6); or it is a generic gradient-and-centred-text hero. Two marked moments, and a work-surface moment over half the screen at 1024 px and wider, are MEDIUM. Fold and size-share checks run on the base rows | HIGH |
140
+
141
+ A lock with `offset` elevation is the recorded neo-brutalist register: DS-007 allows zero-blur ink
142
+ offsets there and on no other lock. A `soft` lock leaves shadows to the craft floor.
143
+
144
+ Media, `img`, `video`, `canvas`, `iframe` and anything inside `[data-lock-exempt]` are skipped. Use
145
+ the exemption only for third-party embeds and real brand marks, and say so in the reply.
146
+
147
+ The DS rule table (severity, tolerance, fix line) lives in `scripts/probe-lock.mjs`; every other
148
+ threshold stays in `scripts/probe-thresholds.json`. Never widen a tolerance or add an exemption to
149
+ make a page pass; change the page.
150
+
151
+ ## 4. Look, then fix
152
+
153
+ After the findings, a rendered probe run prints the paths (and writes them under "Look" in `review.md`) of the 1440, 390 and 320 screenshots
154
+ and six fixed questions (LOOK-1 to LOOK-6: one focal point, shared edges, grouping by space, kit not
155
+ costume, composed mobile, specific content). Open the screenshots, answer each question in one line,
156
+ fix the two worst answers, and run the probe again. Stop after two look rounds; keep the better
157
+ version if a round made it worse. The questions are concrete on purpose: a reviewer, human or
158
+ model, agrees with itself on "do the sections share a left edge" far more often than on "is it
159
+ beautiful".
160
+
161
+ ## 5. Accent art by style
162
+
163
+ The landing starter's art slot takes the locked style's art; work surfaces keep at most a small mark.
164
+
165
+ | Style | Accent art |
166
+ |---|---|
167
+ | `field-notes` | The pixel motif below |
168
+ | `graphite`, `meadow` | A real product view set on a `ui-wash` or between `ui-band` sections, with the second accent on the featured panel |
169
+ | `computational-brutalism` | Instrument parts on real content: a `ui-frame` around the product view or a key figure, `ui-coord` labels that name real positions, sizes or revisions, one `ui-ruler`, `data-coord` on each section (sheet, grid reference or revision, not 01/02/03), a `ui-prompt--cursor` line, and `ui-readout ui-scan` wells on the readouts that carry real values. No made-up telemetry: every number labels something on the page |
170
+ | `neo-brutalism` | `ui-sticker` labels with real facts (price, launch date, a count), never over body text, and colour blocks in all three fills (`ui-panel--accent`, `ui-panel--accent-2`, `ui-panel--support`) |
171
+
172
+ ### Pixel motif (field-notes)
173
+
174
+ On read-once pages the field-notes hero carries an original pixel motif of the product's subject.
175
+ Draw it as a character grid and convert it, rather than hand-placing rectangles:
176
+
177
+ ```bash
178
+ node scripts/pixel-art.mjs motif.txt --label "<what it shows>" --cell 12
179
+ ```
180
+
181
+ Characters map to palette roles (`i` ink, `a` accent, `b` second accent, `m` support, `s` sunken, `p` surface,
182
+ `c` canvas, `l` line-strong, `2` ink-2, `.` empty), so the motif follows dark mode and stays inside
183
+ the lock. Keep it recognisable: a scene of the product's subject, 20 to 64 cells wide, hard edges, as
184
+ many roles as the subject needs, the accent on the one detail that matters. Cells render at 6 px or more. On a
185
+ work surface the motif is at most a small mark beside the product name.
186
+
187
+ ## 6. Signature moment
188
+
189
+ The lock makes runs agree on the look. The signature moment is the one place a page is allowed to be
190
+ bold, so a locked page still has something that makes a person stop. Choose it from the content before
191
+ writing markup, record it in the lock, mark it in the page, and let the probe check it (DS-008).
192
+
193
+ 1. Record it: `init … --moment <kind> --moment-note "<what it shows>" [--moment-place "<where>"]`, or
194
+ later `design-lock.mjs moment --dir <project> --kind <kind> --note "<what it shows>"`. Until a kind
195
+ is recorded the probe fails every render.
196
+ 2. Mark the one element that carries it: `data-ui-moment="<kind>"`.
197
+ 3. Build it from this page's content, inside the lock: palette roles, the kit's type, radii and
198
+ elevation. The moment may use `--fs-moment` (`ui-setpiece`, `ui-bignum`) up to the lock's moment
199
+ ceiling; DS-004 accepts that size only inside `[data-ui-moment]`.
200
+
201
+ | Family | Scale | Kinds | Where |
202
+ |---|---|---|---|
203
+ | `landing` (landing, product, launch, portfolio, docs home, report) | bold | `hero-composition`, `type-set-piece`, `data-moment`, `diagram`, `motion-detail`, `interactive-object` | Starts in the first screen at 1024 px and wider, within 1.5 screens on phones |
204
+ | `workspace`, `settings` | small | `chart`, `empty-state`, `header-detail` | Anywhere; under half the screen at desktop widths. The data stays the focus |
205
+
206
+ | Kind | What it is | DS-008 needs inside it |
207
+ |---|---|---|
208
+ | `hero-composition` | The hero arranged around the product's one concrete object: the invoice, the truck, the score, drawn or built from kit parts at a size that dominates the first screen (`ui-hero--stage`) | An image, drawing, figure, frame or product view |
209
+ | `type-set-piece` | The pitch set as type: a short line at moment size with the accent on the one word that matters, aligned to the grid, never centred over a gradient | Text at display size or above |
210
+ | `data-moment` | One real number or small dataset made big: a `ui-bignum` with its unit and source, a chart that answers the page's question | A chart, table, figure or readout |
211
+ | `diagram` | The product's process or structure drawn once, clearly: inputs, the step the product does, the result | An image, drawing or figure |
212
+ | `motion-detail` | One movement that explains something (a figure counting up to its real value, a step sequence), short, in the style's durations, stopped under reduced motion (RS-003) | An animation or transition (not checked on the reduced-motion row) |
213
+ | `interactive-object` | Something to try on the page: a fee calculator, a toggle between before and after, a slider on a real variable | A control a reader can operate |
214
+ | `chart` | The work surface's main chart, with one annotated marker that names the event a person acts on | A chart, table or readout |
215
+ | `empty-state` | The empty state drawn with care: a small mark from the style, one sentence, the next action | Nothing beyond presence |
216
+ | `header-detail` | The page header's one detail: the product mark, a live status, the plan or period, set with care | Nothing beyond presence |
217
+
218
+ Choose the kind from the content in this order: the one object or number the reader cares about
219
+ (`data-moment`, `hero-composition`); a process they must grasp (`diagram`); words that carry the
220
+ pitch (`type-set-piece`); something worth trying (`interactive-object`); `motion-detail` only when
221
+ movement is the point. State the choice and the note in the reply.
222
+
223
+ Moments by style:
224
+
225
+ | Style | A moment that belongs to it |
226
+ |---|---|
227
+ | `field-notes` | A pixel scene of the product's subject filling the art column (`ui-hero--stage`, cells of 10 to 16 px, the accent on the one detail that matters), or a `ui-setpiece` line in the editorial display with a small pixel mark |
228
+ | `graphite`, `meadow` | On a landing page, the real product view at full size with one live state shown (`ui-panel` parts, `UI.bars`); on a work surface, the main chart with its annotated marker |
229
+ | `computational-brutalism` | An instrument plate on the plot grid: a `ui-frame` holding a `ui-bignum` readout in mono at moment size, a `ui-ruler` and `ui-coord` labels that measure real values, one `ui-scan`; or a heavy uppercase `ui-setpiece` that spans the grid columns |
230
+ | `neo-brutalism` | A stacked block composition: the product object in a `ui-panel--accent` with its offset shadow, more blocks in `ui-panel--accent-2` and `ui-panel--support`, `ui-sticker` labels with real facts, chunky `ui-setpiece` type |
231
+
232
+ Never, in a moment or anywhere: a centred headline over a gradient (DS-008 reports it as a generic
233
+ hero), scattered floating blobs, glass, a stock 3D object, a fake dashboard with invented numbers, or
234
+ a second moment competing with the first. Everything outside the moment stays inside the kit and, in
235
+ the default rich register, may be layered: bands, a wash, the style's texture, the second accent and
236
+ an atmosphere glow.
@@ -2,7 +2,7 @@
2
2
 
3
3
  This guide turns motion into a small, testable part of the interface. Start with one sentence naming the page, target components, and out-of-scope areas; follow it with labeled sections in implementation order. Specify the stack, visual tokens, layers, timing, viewport behavior, reduced-motion behavior, assets, and performance limits before describing a page. Name exact files or components so the implementation does not invent unrelated sections. A page brief may name component families such as hero, pricing, footer, or dashboard before detailing the chosen one.
4
4
 
5
- All values below are starting defaults. Keep the page calm when motion does not explain hierarchy, state, or cause and effect.
5
+ All values below are starting defaults. The rich default layers motion on separate layers so it explains hierarchy, state, or cause and effect; a lock that records calm density (asked for by the brief) keeps the page still. Flash limits and reduced-motion behavior are absolute in both.
6
6
 
7
7
  ## 1. Stack, tokens, and composition
8
8
 
@@ -51,7 +51,7 @@ When a brief explicitly requests scroll-driven choreography, implement a visible
51
51
 
52
52
  ## 3. Motion and resource budgets
53
53
 
54
- **Do:** Give each section one entrance sequence and no more than two simultaneously moving elements. Give the hero one principal scroll or entrance effect and one animated headline. Keep controls responsive while decorative motion runs.
54
+ **Do:** Give each section one entrance sequence and no more than four simultaneously moving elements, each on its own layer (two under a calm lock). Give the hero one principal scroll or entrance effect and one animated headline. Keep controls responsive while decorative motion runs.
55
55
 
56
56
  **Don't:** animate the headline, background, cards, icons, and navigation at once. Remove any motion whose absence does not change comprehension or feedback.
57
57
 
@@ -4,7 +4,9 @@ Use this authored guide for production choices; treat canonical library text as
4
4
 
5
5
  Ask only when two plausible choices would materially change scope, architecture or data behavior, permissions, accessibility, or visual direction. Ask one high-impact question per user turn, explain consequences, wait before choosing or editing, retain answers across rounds, and ask only about unresolved choices. Continue for as many rounds as materially necessary; never re-ask a settled choice. On resume, read the note first. When a brief names competing visual directions but leaves them undecided or unresolved, that axis stays open even if every option is acceptable: acceptance is not a selection or delegation. Give it its own round, and do not announce a default or build before the user picks. Defaults apply only to choices the brief settles, delegates, or leaves immaterial. No visual direction is not an open choice: use the authored pixel profile without asking. For a bounded brief, state the default and continue without routine approval. Review-only and plan-only requests remain read-only. A keyword or bare target does not authorize edits, and standalone deep-interview's planning handoff does not govern this workflow.
6
6
 
7
- The default is an original editorial/pixel composition: large headlines with intentional line breaks, small terminal labels, generous section gaps, fine rules, atmospheric color fields and asymmetric focal placement. Pixel imagery, when useful, must contain visibly discrete grid-aligned cells, limited palette and intentional silhouettes. Smooth vector decoration alone is not the pixel component. Never name or imitate a reference brand, copy its prose or acquire its proprietary fonts/assets. Existing brand constraints override these defaults.
7
+ A new page or screen starts from the design lock in `house-styles.md`: one house style, one layout family and one signature moment, written by `scripts/design-lock.mjs` and built from the kit it generates. Rich density is the default; calm density is chosen only when the brief asks for minimal or calm. The editorial/pixel composition below is the voice of the `field-notes` default and of a brief that names no other direction.
8
+
9
+ The default is an original editorial/pixel composition: large headlines with intentional line breaks, small terminal labels, section gaps set by the locked density (rich by default, calm only on request), fine rules, atmospheric color fields and asymmetric focal placement. Pixel imagery, when useful, must contain visibly discrete grid-aligned cells, limited palette and intentional silhouettes. Smooth vector decoration alone is not the pixel component. Never name or imitate a reference brand, copy its prose or acquire its proprietary fonts/assets. Existing brand constraints override these defaults.
8
10
 
9
11
  Fit the visual profile to the job. Put the information and actions the user came for within easy reach; a large slogan or decorative illustration must not push them out of view. Include practical context that the brief supplies or the page's purpose requires, and mark unknown required details as placeholders instead of inventing them. Match the request language in visible copy. Visual motifs must not displace usable content.
10
12
 
@@ -19,13 +19,13 @@ Run the deterministic probe before making a holistic judgment. A visible signatu
19
19
  | ID | Probe signal and review question | Cheaper fix |
20
20
  | --- | --- | --- |
21
21
  | SLOP-008 | Heading text or gradient stops in the 260–310° hue band with at least 50/255 channel spread, without a declared brand reason. | Use the chosen palette. |
22
- | SLOP-009 | A gradient is clipped into text. | Use a solid readable text colour. |
23
- | SLOP-010 | Recurring chromatic large-blur halos are MEDIUM; a single glow stays under CF-404. | Remove the glow. |
22
+ | SLOP-009 | Gradient text as a reflex: a violet stop, more than one clipped element, or a stop below 4.5:1 (3:1 at 24px or larger) against its surface. One considered gradient headline passes. | Use a solid readable text colour, or one headline with legible stops. |
23
+ | SLOP-010 | Three or more recurring chromatic large-blur halos are MEDIUM; one or two glows stay under CF-404. | Keep a glow for one surface that earns it. |
24
24
  | SLOP-011 | A faint floating sphere or blob has no content referent. | Remove it or give it a real purpose. |
25
25
  | SLOP-012 | Repeating dot/grid texture accompanies another decorative reflex without data behind it. | Remove the texture. |
26
26
  | SLOP-013 | Noise or ornament makes the information hierarchy harder to read. | Strip the decoration first. |
27
27
  | SLOP-014 | Glass blur appears because the component library offers it, with no reason. | Use an opaque surface. |
28
- | SLOP-015 | Repeated coloured edge stripes or pseudo-borders on rounded cards. | Use the card's actual border or remove the stripe. |
28
+ | SLOP-015 | Three or more coloured edge stripes or pseudo-borders on rounded cards; two inset stripes already count. | Use the card's actual border or remove the stripe. |
29
29
 
30
30
  ## Type and colour
31
31
 
@@ -37,7 +37,7 @@ Run the deterministic probe before making a holistic judgment. A visible signatu
37
37
  | SLOP-019 | Too many unrelated type roles appear in one view. | Reuse a named role. |
38
38
  | SLOP-020 | All-caps labels spread beyond a deliberate small role. | Use sentence case. |
39
39
  | SLOP-021 | Gray text loses contrast on a coloured surface; CF-201 owns the ratio. | Correct the text/background pair. |
40
- | SLOP-022 | Several accent hue clusters appear; CF-205 owns the count. | Reserve one accent for interaction. |
40
+ | SLOP-022 | More than four accent hue clusters appear (more than one under a calm lock); CF-205 owns the count. | Give each hue a role: interaction, second accent, status. |
41
41
  | SLOP-023 | Pure black or white base fills flatten a page meant to have a tonal surface. | Use the chosen near-neutral surface. |
42
42
 
43
43
  ## Motion and micro-labels