arkaos 4.28.0 → 4.30.0

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 (151) hide show
  1. package/THE-ARKAOS-GUIDE.md +1 -1
  2. package/VERSION +1 -1
  3. package/arka/skills/refine/SKILL.md +7 -0
  4. package/config/claude-agents/brand-director.md +1 -1
  5. package/config/claude-agents/conversion-strategist.md +1 -1
  6. package/config/claude-agents/design-ops-lead.md +1 -1
  7. package/config/claude-agents/extraction-script-writer.md +1 -1
  8. package/config/claude-agents/frontend-dev.md +1 -1
  9. package/config/claude-agents/landing-cro-specialist.md +1 -1
  10. package/config/claude-agents/ux-designer.md +1 -1
  11. package/config/claude-agents/ux-strategist.md +1 -1
  12. package/config/claude-agents/visual-designer.md +1 -1
  13. package/config/claude-agents/wcag-auditor.md +1 -1
  14. package/config/skills-provenance.yaml +31 -5
  15. package/departments/brand/agents/brand-director.yaml +2 -0
  16. package/departments/brand/agents/design-ops/design-ops-lead.yaml +2 -0
  17. package/departments/brand/agents/design-ops/extraction-script-writer.yaml +1 -0
  18. package/departments/brand/agents/design-ops/wcag-auditor.yaml +2 -0
  19. package/departments/brand/agents/ux-designer.yaml +2 -0
  20. package/departments/brand/agents/ux-strategist.yaml +2 -0
  21. package/departments/brand/agents/visual-designer.yaml +3 -0
  22. package/departments/brand/references/uiux-knowledge-and-tools.md +198 -3
  23. package/departments/brand/skills/colors/SKILL.md +9 -1
  24. package/departments/brand/skills/colors/references/hallmark.LICENSE +21 -0
  25. package/departments/brand/skills/colors/references/impeccable.LICENSE +191 -0
  26. package/departments/brand/skills/colors/references/oklch-theme.md +563 -0
  27. package/departments/brand/skills/design-review/SKILL.md +25 -0
  28. package/departments/brand/skills/design-review/references/anti-patterns.md +420 -0
  29. package/departments/brand/skills/design-review/references/critique-protocol.md +169 -0
  30. package/departments/brand/skills/design-review/references/design-registers.md +181 -0
  31. package/departments/brand/skills/design-review/references/genres/atmospheric.md +71 -0
  32. package/departments/brand/skills/design-review/references/genres/editorial.md +74 -0
  33. package/departments/brand/skills/design-review/references/genres/modern-minimal.md +73 -0
  34. package/departments/brand/skills/design-review/references/genres/playful.md +72 -0
  35. package/departments/brand/skills/design-review/references/hallmark.LICENSE +21 -0
  36. package/departments/brand/skills/design-review/references/impeccable.LICENSE +191 -0
  37. package/departments/brand/skills/design-review/references/impeccable.NOTICE +11 -0
  38. package/departments/brand/skills/design-review/references/slop-test.md +194 -0
  39. package/departments/brand/skills/design-system/SKILL.md +34 -0
  40. package/departments/brand/skills/design-system/references/design-dna-study.md +512 -0
  41. package/departments/brand/skills/design-system/references/design-md-spec.md +454 -0
  42. package/departments/brand/skills/design-system/references/hallmark.LICENSE +21 -0
  43. package/departments/brand/skills/design-system/references/impeccable.LICENSE +191 -0
  44. package/departments/brand/skills/design-system/references/interaction-states.md +306 -0
  45. package/departments/brand/skills/design-system/references/themes/carnival.md +303 -0
  46. package/departments/brand/skills/design-system/references/themes/cobalt.md +148 -0
  47. package/departments/brand/skills/design-system/references/themes/hum.md +405 -0
  48. package/departments/brand/skills/design-system/references/themes/lumen.md +480 -0
  49. package/departments/brand/skills/design-system/references/typography-craft.md +319 -0
  50. package/departments/brand/skills/mockup-generate/SKILL.md +4 -1
  51. package/departments/brand/skills/ux-audit/SKILL.md +25 -4
  52. package/departments/brand/skills/wireframe/SKILL.md +5 -0
  53. package/departments/dev/agents/frontend-dev.yaml +3 -0
  54. package/departments/dev/skills/animated-website/SKILL.md +10 -0
  55. package/departments/dev/skills/animated-website/references/hallmark.LICENSE +21 -0
  56. package/departments/dev/skills/animated-website/references/impeccable.LICENSE +191 -0
  57. package/departments/dev/skills/animated-website/references/motion-recipes.md +546 -0
  58. package/departments/dev/skills/evaluator-build-loop/SKILL.md +5 -0
  59. package/departments/landing/agents/conversion-strategist.yaml +1 -0
  60. package/departments/landing/agents/cro-specialist.yaml +1 -0
  61. package/departments/landing/skills/landing-gen/SKILL.md +16 -5
  62. package/departments/landing/skills/page-architect/SKILL.md +25 -1
  63. package/departments/landing/skills/page-architect/references/component-cookbook.md +265 -0
  64. package/departments/landing/skills/page-architect/references/components/c1-outlined-chip.md +14 -0
  65. package/departments/landing/skills/page-architect/references/components/c2-inline-form-as-cta.md +19 -0
  66. package/departments/landing/skills/page-architect/references/components/c3-typographic-link.md +11 -0
  67. package/departments/landing/skills/page-architect/references/components/c4-sticky-bottom-bar.md +17 -0
  68. package/departments/landing/skills/page-architect/references/components/f1-bento-grid.md +22 -0
  69. package/departments/landing/skills/page-architect/references/components/f2-sticky-scroll-stack.md +23 -0
  70. package/departments/landing/skills/page-architect/references/components/f3-tabular-spec-sheet.md +14 -0
  71. package/departments/landing/skills/page-architect/references/components/f4-step-sequence.md +14 -0
  72. package/departments/landing/skills/page-architect/references/components/f5-annotated-screenshot.md +14 -0
  73. package/departments/landing/skills/page-architect/references/components/f6-product-card-grid.md +42 -0
  74. package/departments/landing/skills/page-architect/references/components/ft1-mast-headed.md +15 -0
  75. package/departments/landing/skills/page-architect/references/components/ft2-inline-rule-single-line.md +13 -0
  76. package/departments/landing/skills/page-architect/references/components/ft3-index-style-category-list.md +15 -0
  77. package/departments/landing/skills/page-architect/references/components/ft4-dense-typographic.md +13 -0
  78. package/departments/landing/skills/page-architect/references/components/ft5-statement.md +24 -0
  79. package/departments/landing/skills/page-architect/references/components/ft6-letter-close.md +22 -0
  80. package/departments/landing/skills/page-architect/references/components/ft7-newsletter-first.md +30 -0
  81. package/departments/landing/skills/page-architect/references/components/ft8-marquee-scroll.md +26 -0
  82. package/departments/landing/skills/page-architect/references/components/h1-marquee.md +17 -0
  83. package/departments/landing/skills/page-architect/references/components/h2-split-diptych.md +18 -0
  84. package/departments/landing/skills/page-architect/references/components/h3-quote-led.md +14 -0
  85. package/departments/landing/skills/page-architect/references/components/h4-stat-led.md +17 -0
  86. package/departments/landing/skills/page-architect/references/components/h5-letter-hero.md +14 -0
  87. package/departments/landing/skills/page-architect/references/components/h6-photographic-fold.md +19 -0
  88. package/departments/landing/skills/page-architect/references/components/h7-demo-video-clipped-by-viewport-edge.md +30 -0
  89. package/departments/landing/skills/page-architect/references/components/h8-mockup-split-browser-framed.md +26 -0
  90. package/departments/landing/skills/page-architect/references/components/h9-custom-illustration-centerpiece.md +28 -0
  91. package/departments/landing/skills/page-architect/references/components/n1-wordmark-2-links.md +14 -0
  92. package/departments/landing/skills/page-architect/references/components/n10-floating-on-scroll-morph.md +20 -0
  93. package/departments/landing/skills/page-architect/references/components/n11-mega-menu.md +43 -0
  94. package/departments/landing/skills/page-architect/references/components/n12-banner-retract.md +37 -0
  95. package/departments/landing/skills/page-architect/references/components/n13-inline-cmdk-pill.md +42 -0
  96. package/departments/landing/skills/page-architect/references/components/n1b-saas-three-section.md +38 -0
  97. package/departments/landing/skills/page-architect/references/components/n2-floating-chip.md +17 -0
  98. package/departments/landing/skills/page-architect/references/components/n3-side-rail.md +17 -0
  99. package/departments/landing/skills/page-architect/references/components/n4-hidden-behind-k.md +12 -0
  100. package/departments/landing/skills/page-architect/references/components/n5-floating-pill.md +31 -0
  101. package/departments/landing/skills/page-architect/references/components/n6-newspaper-masthead.md +27 -0
  102. package/departments/landing/skills/page-architect/references/components/n7-brutal-slab.md +25 -0
  103. package/departments/landing/skills/page-architect/references/components/n8-terminal-command.md +24 -0
  104. package/departments/landing/skills/page-architect/references/components/n9-edge-aligned-minimal.md +20 -0
  105. package/departments/landing/skills/page-architect/references/components/s1-left-margin-numbered.md +17 -0
  106. package/departments/landing/skills/page-architect/references/components/s2-hanging.md +16 -0
  107. package/departments/landing/skills/page-architect/references/components/s3-sticky-pinned.md +22 -0
  108. package/departments/landing/skills/page-architect/references/components/s4-inline-no-break.md +14 -0
  109. package/departments/landing/skills/page-architect/references/components/s5-bottom-anchored.md +14 -0
  110. package/departments/landing/skills/page-architect/references/components/t1-pull-quote-with-marginalia.md +14 -0
  111. package/departments/landing/skills/page-architect/references/components/t2-logo-wall-hairline.md +22 -0
  112. package/departments/landing/skills/page-architect/references/components/t3-single-huge-quote.md +14 -0
  113. package/departments/landing/skills/page-architect/references/components/t4-numbered-stat-strip.md +15 -0
  114. package/departments/landing/skills/page-architect/references/floating-nav.md +91 -0
  115. package/departments/landing/skills/page-architect/references/hallmark.LICENSE +21 -0
  116. package/departments/landing/skills/page-architect/references/hero-enrichment.md +477 -0
  117. package/departments/landing/skills/page-architect/references/impeccable.LICENSE +191 -0
  118. package/departments/landing/skills/page-architect/references/layout-craft.md +236 -0
  119. package/departments/landing/skills/page-architect/references/macrostructures/01-bento-grid.md +37 -0
  120. package/departments/landing/skills/page-architect/references/macrostructures/02-long-document.md +36 -0
  121. package/departments/landing/skills/page-architect/references/macrostructures/03-marquee-hero.md +33 -0
  122. package/departments/landing/skills/page-architect/references/macrostructures/04-stat-led.md +36 -0
  123. package/departments/landing/skills/page-architect/references/macrostructures/05-workbench.md +34 -0
  124. package/departments/landing/skills/page-architect/references/macrostructures/06-conversational-faq.md +35 -0
  125. package/departments/landing/skills/page-architect/references/macrostructures/07-manifesto.md +34 -0
  126. package/departments/landing/skills/page-architect/references/macrostructures/08-photographic.md +36 -0
  127. package/departments/landing/skills/page-architect/references/macrostructures/09-quote-led.md +34 -0
  128. package/departments/landing/skills/page-architect/references/macrostructures/10-specimen.md +34 -0
  129. package/departments/landing/skills/page-architect/references/macrostructures/11-catalogue.md +25 -0
  130. package/departments/landing/skills/page-architect/references/macrostructures/12-letter.md +25 -0
  131. package/departments/landing/skills/page-architect/references/macrostructures/13-index-first.md +25 -0
  132. package/departments/landing/skills/page-architect/references/macrostructures/14-narrative-workflow.md +25 -0
  133. package/departments/landing/skills/page-architect/references/macrostructures/15-split-studio.md +25 -0
  134. package/departments/landing/skills/page-architect/references/macrostructures/16-feature-stack.md +25 -0
  135. package/departments/landing/skills/page-architect/references/macrostructures/17-type-specimen.md +25 -0
  136. package/departments/landing/skills/page-architect/references/macrostructures/18-portfolio-grid.md +25 -0
  137. package/departments/landing/skills/page-architect/references/macrostructures/19-map-diagram.md +25 -0
  138. package/departments/landing/skills/page-architect/references/macrostructures/20-ecosystem-index.md +25 -0
  139. package/departments/landing/skills/page-architect/references/macrostructures/21-component-playground.md +25 -0
  140. package/departments/landing/skills/page-architect/references/macrostructures.md +91 -0
  141. package/departments/landing/skills/page-architect/references/ui-copy.md +75 -0
  142. package/harness/codex/AGENTS.md +1 -1
  143. package/harness/copilot/copilot-instructions.md +1 -1
  144. package/harness/cursor/rules/arkaos.mdc +2 -2
  145. package/harness/gemini/GEMINI.md +1 -1
  146. package/harness/opencode/AGENTS.md +1 -1
  147. package/harness/zed/.rules +1 -1
  148. package/knowledge/agents-registry-v2.json +27 -8
  149. package/knowledge/skills-manifest.json +16 -6
  150. package/package.json +1 -1
  151. package/pyproject.toml +1 -1
@@ -0,0 +1,306 @@
1
+ # Interaction and states
2
+
3
+ > Source: merge of `references/interaction-and-states.md` from the **hallmark** skill repo (MIT — see `hallmark.LICENSE` in this references directory) and `skill/reference/interaction-design.md` from the **impeccable** repo (Apache-2.0 — see `impeccable.LICENSE` in this references directory). The eight-state model and input-field rules are hallmark's; keyboard-navigation patterns, gesture discoverability, and the dropdown-clipping/anchor-positioning material are impeccable's. Rule anchors follow impeccable's `<!-- rule:… -->` convention (the two source files carried none; the IDs below are minted for this port).
4
+
5
+ Every interactive element has eight states. Most AI-generated UI styles two (default, hover) and forgets the rest. That's where interfaces break.
6
+
7
+ ## The eight states
8
+
9
+ | State | When | Treatment |
10
+ | --- | --- | --- |
11
+ | Default | At rest | Base styling |
12
+ | Hover | Pointer over (only with `@media (hover: hover)`) | Small shift: colour, 1px translate, subtle border |
13
+ | Focus | Keyboard or programmatic focus | Visible ring, `:focus-visible` |
14
+ | Active / Pressed | During press | Pressed-in: darker, translate(0 1px) |
15
+ | Disabled | Not interactive | Reduced opacity (0.5) + `cursor: not-allowed` + `aria-disabled` |
16
+ | Loading | Processing | Inline spinner or progress, label stays readable |
17
+ | Error | Failed state | Red border, error icon, message, `aria-invalid` |
18
+ | Success | Completed | Green check, confirmation, auto-dismiss |
19
+
20
+ If any of these is missing on a production element, the element isn't finished. <!-- rule:interaction-eight-states -->
21
+
22
+ **The common miss**: designing hover without focus, or vice versa. They're different states — keyboard users never see hover states. <!-- rule:interaction-hover-focus-distinct -->
23
+
24
+ ## Focus rings
25
+
26
+ Visible, always, on every interactive element. The default focus ring most browsers give you is fine; a custom one is better.
27
+
28
+ ```css
29
+ :focus { outline: none; }
30
+ :focus-visible {
31
+ outline: 2px solid var(--color-focus);
32
+ outline-offset: 2px;
33
+ border-radius: inherit;
34
+ }
35
+ ```
36
+
37
+ Requirements:
38
+
39
+ - 2–3px, ≥ 3:1 contrast against both element and page.
40
+ - 2px offset from the element (not inside it).
41
+ - `:focus-visible`, not `:focus`, so it's keyboard-only.
42
+ - Consistent across all interactive elements.
43
+ - Never `outline: none` without a replacement. `outline: none` with no other focus style is the most common accessibility bug and an immediate audit failure. <!-- rule:interaction-no-bare-outline-none -->
44
+
45
+ ## Hit targets
46
+
47
+ Minimum 44×44 CSS px for any touch-reachable element. <!-- rule:interaction-hit-target-44 --> Use padding or an `::before` overlay to expand the hit target without changing visual size:
48
+
49
+ ```css
50
+ .icon-btn {
51
+ position: relative;
52
+ }
53
+ .icon-btn::before {
54
+ content: "";
55
+ position: absolute;
56
+ inset: -12px;
57
+ }
58
+ ```
59
+
60
+ ## Forms
61
+
62
+ - Labels above inputs. Visible. Never placeholder-as-label — placeholders disappear on input. <!-- rule:interaction-no-placeholder-label -->
63
+ - Placeholders show format, not instruction. `Placeholder: 01 Jan 2026`, not `Placeholder: Enter your birth date`.
64
+ - Helper text below input. Error text replaces helper text.
65
+ - Validate on **blur**, not on every keystroke (exception: password strength). Revalidate on change once the field has been blurred once (the "touched" pattern). <!-- rule:interaction-validate-on-blur -->
66
+ - Error message: (1) what broke, (2) why, (3) what to do. One sentence if possible.
67
+ - Associate errors with `aria-describedby`. Set `aria-invalid="true"` on the field.
68
+ - Required fields marked with `aria-required`, never with colour alone.
69
+ - Disable the submit button only when the form is in a known-invalid or in-flight state. Never on idle.
70
+
71
+ ## Input field states — the exhaustive checklist
72
+
73
+ This is where the most "almost right" UIs lose. An input field with two states (default + hover) and a different border-width on focus reads as a default settings page — the geometry shifts, the eye notices, the page feels untuned. Every text input, textarea, select, and combobox must satisfy every rule below.
74
+
75
+ ### The no-layout-shift rule
76
+
77
+ **Border thickness is constant across every state.** Default · hover · focus · error · disabled — the `border-width` value never changes. Layout shift on focus is a tell. State changes go to `background-color`, `outline`, or `box-shadow`, never to `border-width`. <!-- rule:interaction-constant-border-width -->
78
+
79
+ ```css
80
+ .input {
81
+ border: 1px solid var(--color-rule-2); /* 1px, always — every state */
82
+ outline: 2px solid transparent; /* reserved slot for focus ring; no shift on activate */
83
+ outline-offset: 1px;
84
+ }
85
+ ```
86
+
87
+ The outline starts transparent at 2 px so when the focus ring appears, the box geometry is already correct. No layout shift. No paint thrash.
88
+
89
+ ### State-by-state recipe
90
+
91
+ | State | Treatment | Why |
92
+ | --- | --- | --- |
93
+ | **Default** | `border: 1px solid var(--color-rule-2)` · `background: var(--color-paper)` · placeholder in `var(--color-muted)` | Visible field, readable empty signal |
94
+ | **Hover** | `background: var(--color-paper-2)` (4–6 % darker than paper) · border unchanged | Subtle background shift, no border flash. Border colour changing alone is missable. |
95
+ | **Focus** | `outline: 2px solid var(--color-focus)` · `outline-offset: 1px` · border may deepen to `var(--color-ink-2)` but width stays 1 px | Outline is the focus signal; never animated; ≥ 3:1 contrast against page AND field. |
96
+ | **Active / typing** | Same as focus. Don't add a separate "typing" state. | Focus already says "active here". A second signal is noise. |
97
+ | **Filled** | Same as default — the value carries the state. Optionally a subtle ink-2 border to visually distinguish from empty. | Don't fight the user's content with a styled chrome change. |
98
+ | **Disabled** | `opacity: 0.55` · `cursor: not-allowed` · placeholder `var(--color-rule-2)` · `aria-disabled="true"` · `tabindex="-1"` | Three independent signals (opacity + cursor + colour) so no single channel carries the whole load. |
99
+ | **Error** | `border-color: var(--color-error, oklch(58% 0.20 25))` · helper-text replaced by error message · `aria-invalid="true"` · small ⚠ glyph at right edge | Border colour flip is OK *because* helper-text and aria signal it too. Never colour alone. |
100
+ | **Success** | Subtle accent-coloured border (3 % chroma above default) · small ✓ glyph · auto-clear if user re-edits | Quiet; success doesn't deserve celebration unless it was hard. |
101
+ | **Loading** (validating, async) | Inline spinner at the right edge replacing the standard glyph slot · field stays editable but submit disabled | Don't lock the user out of the field. They may want to fix what they typed. |
102
+
103
+ ### Heights and rhythm
104
+
105
+ - **Input height = button height.** A page with 44 px buttons and 38 px inputs feels untuned. Pick one base height (44 px is the touch-target floor) and apply it to every text input AND every adjacent button. <!-- rule:interaction-input-button-height -->
106
+ - **Vertical padding = `(height − line-height-px) / 2`.** No magic numbers.
107
+ - **Right-edge slot reserved.** Every input reserves a ~24 px right-edge slot for an optional clear button, error glyph, or loading spinner. If unused, the slot sits empty — never reflow on icon appearance.
108
+
109
+ ### Labels, helper, error
110
+
111
+ - **Label above** the input, 4–8 px gap. Never inline (placeholder-as-label is a tell).
112
+ - **Helper text below**, ~4 px gap. Same `font-size` as the label, lower visual weight.
113
+ - **Error replaces helper** — same position, same size, error colour. Never both at once (causes vertical jump on validation).
114
+ - **Helper has stable height.** Reserve a 1-line height even when empty, so adding an error doesn't push the page down. CSS: `min-height: 1lh` on the helper container.
115
+
116
+ ### Don't, list
117
+
118
+ - Don't transition `border-width`, `padding`, or `height` on any state. Always layout-shift.
119
+ - Don't transition the focus ring's `opacity` or `transform`. Focus must be instant.
120
+ - Do put hover effects inside `@media (hover: hover)` so touch users don't get stuck states.
121
+ - Don't disable the field as a way to indicate "wait, loading" — use a loading state with the field still editable.
122
+ - Don't change `cursor` on `:focus`. The pointer is already a beam; don't fight it.
123
+ - Don't use `outline: none` on focus without an explicit replacement.
124
+
125
+ ### Specific control overrides
126
+
127
+ - **Textarea.** Same rules as input, plus `resize: vertical` (never `none`, never `both` on a small textarea), `min-height: 6rem` for multi-line UX.
128
+ - **Select.** Custom-styled `<select>` only if you can replicate native a11y (keyboard, screen-reader). Otherwise leave it native and style the wrapper.
129
+ - **Checkbox / radio.** Use `accent-color: var(--color-accent)` for cheap correct styling on modern browsers; only build a custom one when the design requires it. If custom: still a 1 px outline-offset focus ring.
130
+ - **Toggle / switch.** It IS a checkbox. Same a11y rules. The visual design doesn't change the contract.
131
+ - **Range / slider.** The thumb gets focus state, not the track. Thumb hit-target ≥ 44 px even if visual size is smaller (use a transparent expansion).
132
+ - **File input.** Always wrap in a styled label. Native `<input type="file">` is unstyleable; the label is the surface.
133
+ - **Combobox / search.** Listbox sits below, `aria-expanded` mirrors visibility, arrow-keys cycle, Enter selects, Escape closes — and the listbox doesn't push page content (use `position: absolute` + a parent `position: relative`).
134
+
135
+ ## Modals and overlays
136
+
137
+ - Use the native `<dialog>` element. It handles focus trap, escape to close, and `::backdrop` styling for free (`dialog.showModal()`).
138
+ - **Centre it explicitly.** A `<dialog>` opened with `showModal()` centres on its own — but the moment you add custom positioning (a `margin-top`, a `top`, a stray `transform`) it can snap to the viewport's top-left corner. Pin it: `position: fixed; inset: 0; margin: auto; height: fit-content; max-height: min(80vh, 40rem);`. A non-`<dialog>` overlay centres via its container instead: `position: fixed; inset: 0; display: grid; place-items: center;`. Never ship a modal or command palette stuck in the corner. <!-- rule:interaction-modal-centred -->
139
+ - Set `inert` on the page content behind a modal so tab order doesn't leak — the modern replacement for hand-rolled focus-trap JavaScript:
140
+
141
+ ```html
142
+ <!-- When modal is open -->
143
+ <main inert>
144
+ <!-- Content behind modal can't be focused or clicked -->
145
+ </main>
146
+ <dialog open>
147
+ <h2>Modal Title</h2>
148
+ <!-- Focus stays inside modal -->
149
+ </dialog>
150
+ ```
151
+
152
+ - Close on: escape key, backdrop click, explicit close button.
153
+ - First focus goes to the first interactive element, not the close button.
154
+
155
+ ## Dropdowns, tooltips, popovers
156
+
157
+ - Use the Popover API (`popover` attribute). It handles light-dismiss (click outside closes), stacking, and escape for free, works in every modern browser, and places the element in the **top layer** — above all other content regardless of z-index or overflow. No z-index wars, no portal needed, accessible by default.
158
+
159
+ ```html
160
+ <button popovertarget="menu">Open menu</button>
161
+ <div id="menu" popover>
162
+ <button>Option 1</button>
163
+ <button>Option 2</button>
164
+ </div>
165
+ ```
166
+
167
+ - Position with CSS Anchor Positioning where it's available; fall back to `position: fixed` + `getBoundingClientRect()`.
168
+ - Never put a dropdown inside an `overflow: hidden` container without escape. It will clip.
169
+ - Flip when near the viewport edge.
170
+
171
+ ### The dropdown-clipping rule
172
+
173
+ Dropdowns rendered with `position: absolute` inside a container that has `overflow: hidden` or `overflow: auto` will be clipped. This is the single most common dropdown bug in generated code. <!-- rule:interaction-dropdown-no-overflow-clip -->
174
+
175
+ **CSS Anchor Positioning.** The modern solution tethers an overlay to its trigger without JavaScript:
176
+
177
+ ```css
178
+ .trigger {
179
+ anchor-name: --menu-trigger;
180
+ }
181
+
182
+ .dropdown {
183
+ position: fixed;
184
+ position-anchor: --menu-trigger;
185
+ position-area: block-end span-inline-end;
186
+ margin-top: 4px;
187
+ }
188
+
189
+ /* Flip above if no room below */
190
+ @position-try --flip-above {
191
+ position-area: block-start span-inline-end;
192
+ margin-bottom: 4px;
193
+ }
194
+ ```
195
+
196
+ Because the dropdown uses `position: fixed`, it escapes any `overflow` clipping on ancestor elements. The `@position-try` block handles viewport edges automatically. **Browser support**: Chrome 125+, Edge 125+. Not yet in Firefox or Safari — use a fallback for those browsers.
197
+
198
+ **Popover + anchor combo.** Combining the Popover API with anchor positioning gives you stacking, light-dismiss, accessibility, and correct positioning in one pattern — the `popover` attribute supplies the top layer, the anchor supplies the tether.
199
+
200
+ **Portal / Teleport pattern.** In component frameworks, render the dropdown at the document root and position it with JavaScript:
201
+
202
+ - **React**: `createPortal(dropdown, document.body)`
203
+ - **Vue**: `<Teleport to="body">`
204
+ - **Svelte**: use a portal library or mount to `document.body`
205
+
206
+ Calculate position from the trigger's `getBoundingClientRect()`, then apply `position: fixed` with `top` and `left` values. Recalculate on scroll and resize.
207
+
208
+ **Fixed positioning fallback.** For browsers without anchor-positioning support, `position: fixed` with manual coordinates avoids overflow clipping. Check viewport boundaries before rendering: if the dropdown would overflow the bottom edge, flip it above the trigger; if it would overflow the right edge, align it to the trigger's right side instead.
209
+
210
+ ## Undo over confirm
211
+
212
+ - For reversible actions, skip the confirm dialog — users click through confirmations mindlessly. Do the thing. Show a toast with an Undo button for 5–10 seconds; actually delete after the toast expires. <!-- rule:interaction-undo-over-confirm -->
213
+ - For destructive, irreversible actions (delete account, drop table), high-cost actions, or batch operations, keep the confirm — and make the user type the thing being destroyed, not just click "OK".
214
+
215
+ ## Keyboard navigation patterns
216
+
217
+ ### Roving tabindex
218
+
219
+ For component groups (tabs, menu items, radio groups), one item is tabbable; arrow keys move within: <!-- rule:interaction-roving-tabindex -->
220
+
221
+ ```html
222
+ <div role="tablist">
223
+ <button role="tab" tabindex="0">Tab 1</button>
224
+ <button role="tab" tabindex="-1">Tab 2</button>
225
+ <button role="tab" tabindex="-1">Tab 3</button>
226
+ </div>
227
+ ```
228
+
229
+ Arrow keys move `tabindex="0"` between items. Tab moves to the next component entirely.
230
+
231
+ ### Skip links
232
+
233
+ Provide skip links (`<a href="#main-content">Skip to main content</a>`) for keyboard users to jump past navigation. Hide off-screen, show on focus. <!-- rule:interaction-skip-links -->
234
+
235
+ ## Gesture discoverability
236
+
237
+ Swipe-to-delete and similar gestures are invisible. Hint at their existence:
238
+
239
+ - **Partially reveal**: show the delete button peeking from the edge.
240
+ - **Onboarding**: coach marks on first use.
241
+ - **Alternative**: always provide a visible fallback (menu with "Delete").
242
+
243
+ Don't rely on gestures as the only way to perform actions. <!-- rule:interaction-gesture-fallback -->
244
+
245
+ ## Loading and empty states
246
+
247
+ - **Skeleton** screens over spinners for content that has a predictable shape (lists, cards, tables) — they preview the content shape and feel faster than generic spinners.
248
+ - **Inline spinners** for in-button state. Replace the label, don't add beside it.
249
+ - **Optimistic updates**: show success immediately, roll back on failure. Use for low-stakes actions (likes, follows), not payments or destructive actions. <!-- rule:interaction-optimistic-low-stakes -->
250
+ - **Empty states** always have: an illustration or icon (a small one), a one-line explanation of why it's empty, an action to fix it.
251
+ - Never show a generic "No results" with no context.
252
+
253
+ ## Bans
254
+
255
+ - Placeholder-as-label.
256
+ - Hover-only functionality (touch users can't hover).
257
+ - Focus rings removed without replacement.
258
+ - Confirmation dialogs for low-stakes actions.
259
+ - Touch targets < 44px.
260
+ - Custom cursors on interactive elements.
261
+ - Disabled elements with no explanation of why they're disabled.
262
+ - Colour-only error states.
263
+ - Spinners where a skeleton would show layout.
264
+ - Generic error messages.
265
+ - Custom controls without ARIA/keyboard support.
266
+
267
+ ---
268
+
269
+ ## Contrast discipline
270
+
271
+ Output must pass the contrast gates of the design-review skill (`departments/brand/skills/design-review/` — upstream: hallmark slop-test gates 40–41) before shipping. Compute contrast for every `(color, background-color)` pair on the page. The common failures generated output trips on:
272
+
273
+ 1. **Text on a flipped surface.** `.section--ink { background: var(--color-ink); }` flips the surface dark; nested text still inherits `color: var(--color-ink)` → ink-on-ink. Fix: any rule that sets a dark `background` must *also* set `color: var(--color-paper)` in the same rule.
274
+ 2. **Button text on accent fill.** `background: var(--color-accent); color: white;` — but white is 4.5:1 against this accent only if `--color-accent` is dark enough. Use `var(--color-accent-ink)` instead, which the theme guarantees passes ≥ APCA Lc 60.
275
+ 3. **Muted text on tinted paper.** `color: var(--color-muted); background: var(--color-paper-3);` — both mid-lightness, often falls below 4.5:1. Use `--color-neutral` (darker) or lift the background to `--color-paper`.
276
+ 4. **Focus ring on accent-coloured button.** `outline: 2px solid var(--color-focus);` on a button whose fill is `--color-accent` — if `--color-focus = --color-accent`, the ring vanishes. Use the contrast pair: `--color-focus` set to a colour with ≥ 3:1 against both the element and the page.
277
+
278
+ ### Computation
279
+
280
+ For each `(text-colour, background-colour)` pair the page actually renders:
281
+
282
+ - Run **APCA Lc** (preferred — perceptual) or **WCAG 2.1 ratio**.
283
+ - Pre-check: if both are in OKLCH with `|L_a − L_b| < 50 %`, flag for full check.
284
+ - Body text passes at **APCA Lc ≥ 60** ≈ WCAG 4.5:1.
285
+ - Large text / focus rings / icons pass at **APCA Lc ≥ 45** ≈ WCAG 3:1.
286
+
287
+ ### Token contract
288
+
289
+ Every theme MUST define `--color-accent-ink` — the text colour to use whenever `--color-accent` fills a surface that carries text. The accent-ink colour is verified ≥ APCA Lc 60 against the accent at the time the theme is built. Code that uses `background: var(--color-accent)` must also set `color: var(--color-accent-ink)`. Falling back to hardcoded `color: white` is a tell — the theme's accent could be a light colour, and white-on-light is the bug. <!-- rule:interaction-accent-ink-contract -->
290
+
291
+ ### When the surface flips
292
+
293
+ The rule: **any rule that overrides `background-color` must also state the appropriate `color`.** Don't rely on inheritance for surface-flipping classes. <!-- rule:interaction-surface-flip-color --> Example:
294
+
295
+ ```css
296
+ /* WRONG — text inherits color: var(--color-ink); section is now dark; ink-on-ink */
297
+ .section--manifesto { background: var(--color-ink); }
298
+
299
+ /* RIGHT */
300
+ .section--manifesto {
301
+ background: var(--color-ink);
302
+ color: var(--color-paper);
303
+ }
304
+ ```
305
+
306
+ Same applies to per-theme overrides like `[data-theme="manifesto"] .vs__col:first-child { background: var(--color-ink); }` — set `color: var(--color-paper)` at the same time, OR declare the rule on a parent and let descendants inherit explicitly.
@@ -0,0 +1,303 @@
1
+ # Theme — Carnival
2
+
3
+ > Source: verbatim port of `references/themes/carnival.md` from the **hallmark** skill repo (MIT — see `hallmark.LICENSE` one directory up, in the references root). Sanitized for ArkaOS: diversification-log path under `.arka/design/`, design stamps rendered as `[arka:design-dna]`. Upstream cross-references (`site/css/tokens.css`, `site/examples/*`, `custom-theme.md`, `layout-and-space.md`, slop-test gate numbers) name hallmark repo files that are not bundled here and travel as plain text.
4
+
5
+ Loud-maximalist editorial. **Duo-tone accent system across six named palette drops** (each its own mood), chunky variable display, decorative ornaments, hard-offset shadows, tinted paper. The loud sibling to Riso / Manifesto / Brutal — but **decorative, not raw**.
6
+
7
+ Loaded eagerly by the design-system skill whenever the catalog pick is `carnival`. The default palette ("Cold Snap") and font stack live in `site/css/tokens.css` (upstream hallmark repo — not bundled here) under `[data-theme="carnival"]`; this file carries the **palette drops** (six variants) plus the rules that tokens cannot encode.
8
+
9
+ ## Axes (diversification)
10
+
11
+ - **Paper band** — light (`L 88–95%`, tinted — varies by drop)
12
+ - **Display style** — **display-heavy** (Big Shoulders Display 800, variable-width axis)
13
+ - **Accent hue** — **per-drop**. Each drop has its own duo-tone hue pair (warm+warm, cool+warm, warm+cool, etc.). The drop name is recorded alongside the theme name in the diversification log so consecutive builds rotate drops, not just themes.
14
+
15
+ ## Palette drops
16
+
17
+ A drop is a named duo-tone palette that preserves Carnival's structural signature (saturated accent-1 + complementary accent-2 + tinted paper + deep ink) while rotating hue. **Every Carnival build picks one drop.** The catalog of six:
18
+
19
+ ### Drop 01 · Cold Snap *(default)*
20
+
21
+ Warm + warm. Indie record-label, winter, scrappy. The canonical Carnival.
22
+
23
+ - `--color-paper: oklch(92% 0.045 50)` — warm pink-cream
24
+ - `--color-paper-2: oklch(88% 0.050 45)`
25
+ - `--color-paper-3: oklch(82% 0.060 40)`
26
+ - `--color-ink: oklch(18% 0.080 20)` — deep aubergine
27
+ - `--color-ink-2: oklch(28% 0.060 25)`
28
+ - `--color-muted: oklch(45% 0.05 30)`
29
+ - `--color-rule: oklch(40% 0.18 25)` — oxblood rules (decorative)
30
+ - `--color-accent: oklch(86% 0.18 95)` — mustard
31
+ - `--color-accent-2: oklch(40% 0.21 25)` — oxblood
32
+ - `--color-accent-ink: oklch(18% 0.080 20)`
33
+ - `--color-focus: oklch(40% 0.21 25)`
34
+
35
+ **When to pick:** independent music · winter releases · DIY scrappy · the brief mentions cassettes, vinyl, EPs.
36
+
37
+ ### Drop 02 · Citrus Riot
38
+
39
+ Loud + neon. 90s zine, summer, electric. Lime against magenta — the hardest collision in the catalogue.
40
+
41
+ - `--color-paper: oklch(94% 0.040 85)` — pale acid cream
42
+ - `--color-paper-2: oklch(90% 0.048 82)`
43
+ - `--color-paper-3: oklch(84% 0.055 80)`
44
+ - `--color-ink: oklch(20% 0.07 145)` — deep forest
45
+ - `--color-ink-2: oklch(32% 0.05 140)`
46
+ - `--color-muted: oklch(48% 0.04 130)`
47
+ - `--color-rule: oklch(28% 0.28 350)` — deep magenta rules
48
+ - `--color-accent: oklch(82% 0.20 130)` — chartreuse-lime
49
+ - `--color-accent-2: oklch(28% 0.28 350)` — deep magenta (reads AA on paper, and as text)
50
+ - `--color-accent-ink: oklch(20% 0.07 145)`
51
+ - `--color-focus: oklch(28% 0.28 350)`
52
+
53
+ **When to pick:** zine collectives · summer drops · skate / DIY culture · briefs that want LOUD without referencing music.
54
+
55
+ ### Drop 03 · Diner Sign
56
+
57
+ Americana. Cream + cherry red + navy. Postwar diner, road trip, neon-and-chrome.
58
+
59
+ - `--color-paper: oklch(95% 0.035 90)` — bright cream
60
+ - `--color-paper-2: oklch(91% 0.042 88)`
61
+ - `--color-paper-3: oklch(86% 0.050 85)`
62
+ - `--color-ink: oklch(16% 0.04 30)` — black-brown
63
+ - `--color-ink-2: oklch(28% 0.05 30)`
64
+ - `--color-muted: oklch(45% 0.04 35)`
65
+ - `--color-rule: oklch(30% 0.16 250)` — navy rules
66
+ - `--color-accent: oklch(60% 0.22 25)` — cherry red
67
+ - `--color-accent-2: oklch(30% 0.16 250)` — navy
68
+ - `--color-accent-ink: oklch(95% 0.035 90)`
69
+ - `--color-focus: oklch(30% 0.16 250)`
70
+
71
+ **When to pick:** food + drink · hospitality · vintage Americana · briefs mentioning burgers, milkshakes, motels, roadside.
72
+
73
+ ### Drop 04 · Studio Night
74
+
75
+ Cool + cool. Dusk warmth, cyan + plum. Late-night booth, blue hour, podcast studio at midnight.
76
+
77
+ - `--color-paper: oklch(88% 0.05 25)` — warm dusk pink
78
+ - `--color-paper-2: oklch(84% 0.055 22)`
79
+ - `--color-paper-3: oklch(78% 0.06 20)`
80
+ - `--color-ink: oklch(20% 0.05 270)` — deep navy-black
81
+ - `--color-ink-2: oklch(32% 0.045 265)`
82
+ - `--color-muted: oklch(48% 0.04 260)`
83
+ - `--color-rule: oklch(24% 0.18 320)` — deep plum rules
84
+ - `--color-accent: oklch(78% 0.18 220)` — cyan
85
+ - `--color-accent-2: oklch(24% 0.18 320)` — deep plum (reads AA on paper, and as text)
86
+ - `--color-accent-ink: oklch(20% 0.05 270)`
87
+ - `--color-focus: oklch(24% 0.18 320)`
88
+
89
+ **When to pick:** late-night radio · podcasts about anything · music + atmosphere · briefs mentioning "late", "after dark", "blue hour", "moonlight".
90
+
91
+ ### Drop 05 · Aqua Park
92
+
93
+ Cool + warm. Turquoise against coral. Summer pool, motel sign, vacation.
94
+
95
+ - `--color-paper: oklch(94% 0.040 180)` — pale aqua-cream
96
+ - `--color-paper-2: oklch(90% 0.048 178)`
97
+ - `--color-paper-3: oklch(84% 0.055 175)`
98
+ - `--color-ink: oklch(20% 0.06 200)` — deep teal
99
+ - `--color-ink-2: oklch(32% 0.05 198)`
100
+ - `--color-muted: oklch(48% 0.04 195)`
101
+ - `--color-rule: oklch(36% 0.24 35)` — deep coral rules
102
+ - `--color-accent: oklch(72% 0.16 195)` — turquoise
103
+ - `--color-accent-2: oklch(36% 0.24 35)` — deep coral (reads AA on paper, and as text)
104
+ - `--color-accent-ink: oklch(20% 0.06 200)`
105
+ - `--color-focus: oklch(36% 0.24 35)`
106
+
107
+ **When to pick:** summer brands · vacation / hospitality · skate / surf / pool · briefs mentioning summer, beach, motel, sun.
108
+
109
+ ### Drop 06 · Pressroom
110
+
111
+ Warm + cool. Amber-gold against slate-blue. 1950s journalism, print shop, broadsheet weight.
112
+
113
+ - `--color-paper: oklch(89% 0.025 65)` — warm slate-cream
114
+ - `--color-paper-2: oklch(85% 0.030 62)`
115
+ - `--color-paper-3: oklch(79% 0.035 58)`
116
+ - `--color-ink: oklch(16% 0.02 60)` — ink-black
117
+ - `--color-ink-2: oklch(28% 0.025 58)`
118
+ - `--color-muted: oklch(45% 0.025 55)`
119
+ - `--color-rule: oklch(34% 0.10 240)` — slate-blue rules
120
+ - `--color-accent: oklch(78% 0.18 75)` — amber-gold
121
+ - `--color-accent-2: oklch(34% 0.10 240)` — slate-blue
122
+ - `--color-accent-ink: oklch(16% 0.02 60)`
123
+ - `--color-focus: oklch(34% 0.10 240)`
124
+
125
+ **When to pick:** journalism · newsletters · editorial / opinion · briefs mentioning "press", "newspaper", "broadsheet", "subscription".
126
+
127
+ ### Drop rotation rule
128
+
129
+ The diversification log (`.arka/design/log.json`) records the drop alongside the theme: `"theme": "carnival", "drop": "studio-night"`. **A new Carnival build picks a drop that hasn't appeared in the last 3 entries.** If only Cold Snap is in the log, any of the other five is valid. If the brief contains a strong drop signal (see "when to pick" above), honour the signal even if it tightens diversification.
130
+
131
+ ### Pick the drop by domain first
132
+
133
+ Match the brief's **domain** before reaching for the loudest palette:
134
+
135
+ - food / drink / hospitality / street market → **Diner Sign** (or Cold Snap)
136
+ - independent music / labels / EPs → **Cold Snap** (or Pressroom)
137
+ - late-night / radio / podcast / after-dark → **Studio Night**
138
+ - summer / pool / beach / vacation → **Aqua Park**
139
+ - zine / skate / DIY / deliberately chaotic → **Citrus Riot**
140
+ - journalism / newsletter / editorial → **Pressroom**
141
+
142
+ Citrus Riot and Aqua Park are the highest-chroma drops — reach for them when the brief genuinely *wants* maximum loudness, not as a default. A food market reads better in Diner Sign than in lime-on-magenta.
143
+
144
+ ### When to construct a custom drop instead
145
+
146
+ If the brief explicitly names a brand colour that doesn't fit any catalog drop — e.g. *"our brand is teal and beige"* — route to the **custom theme** branch (see `custom-theme.md`) instead of stretching a Carnival drop. Drops are curated, not infinite. Six is the right number for now.
147
+
148
+ ## Reference register
149
+
150
+ Dropout TV · Fly.io · Stones Throw Records · Third Man Records · Drag City · Moodelier · Kelsey Dake · Bold Monday.
151
+
152
+ The aesthetic: independent music labels, comedy networks, illustrator portfolios, hot-sauce brands, indie game studios. Things with **character**. Things that print posters as a side hustle. Things that sound loud out loud.
153
+
154
+ **Patron-saint reference (internal):** *Dropout TV homepage* + *Stones Throw artist pages*. When in doubt about decorative density, ask "would Dropout or Stones Throw run this much density?" If less, add ornaments. If more, you've gone too far.
155
+
156
+ ## Signature moves
157
+
158
+ The theme's seven tics. A Carnival build should exhibit at least five of them.
159
+
160
+ 1. **Duo-tone accent system.** Sections alternate which accent fills them. Section 1 fills with `--color-accent` (mustard); section 2 fills with `--color-accent-2` (oxblood); section 3 mustard again; etc. One section never shows both — they are *competing*, not blended.
161
+
162
+ ```css
163
+ .section:nth-of-type(odd) { --section-fill: var(--color-accent); }
164
+ .section:nth-of-type(even) { --section-fill: var(--color-accent-2); }
165
+ ```
166
+
167
+ 2. **Decorative ornaments.** Use `✱` (asterisk operator), `❋` (heavy six-petalled), `◆` (black diamond) as bullets, section dividers, and rhythmic spacers. Patterns:
168
+ - Section dividers: `✱ ✱ ✱ ✱` (repeated 4×, centred)
169
+ - List bullets: `❋` (replaces • / disc)
170
+ - Section heading prefixes: `◆ Section Name` (left-aligned, ornament in accent colour)
171
+
172
+ 3. **Layered colour blocks that bleed off the page edge.** Accent fills extend 24px past the page max-width on the left or right, so they read as **posters pinned to the wall**, not as buttons.
173
+
174
+ ```css
175
+ .colour-block { margin-inline: calc(var(--page-gutter) * -1); padding-inline: var(--page-gutter); }
176
+ ```
177
+
178
+ 4. **Hard-offset drop shadow.** Every card, CTA, and image gets `box-shadow: 4px 4px 0 var(--color-ink)`. No soft shadows ever. The shadow is **flat ink, offset**. This is the single most-recognisable Carnival move.
179
+
180
+ 5. **All-caps headlines with Big Shoulders variable-width, tightly tracked.** `font-variation-settings: "wdth" 110, "wght" 800;`. Track them **tight**, not loose: `letter-spacing: -0.005em` on the hero word, `0.02em` on section heads. The loose `0.04em` look reads as AI-spread and is reserved **only** for the marquee banner (where horizontal spread is the point). Keep line-height tight too: `0.82` for single hero words, `0.92` for multi-line heads. The effect is a marquee poster set by a typographer, not a stretched default.
181
+
182
+ 6. **Halftone pattern fills** in placeholder image regions. Pure CSS, no images:
183
+
184
+ ```css
185
+ .halftone {
186
+ background-image: radial-gradient(var(--color-ink) 1.5px, transparent 1.5px);
187
+ background-size: 12px 12px;
188
+ }
189
+ ```
190
+
191
+ Used wherever the page would otherwise have a photo placeholder.
192
+
193
+ 7. **Marquee scroll** on banner OR footer with decorative dot separators. *"NEW EP ◆ OUT NOW ◆ ON CASSETTE ◆ BLUE VINYL ◆ NEW EP ◆..."* — horizontal scroll, honours `prefers-reduced-motion: reduce` (freeze at static state).
194
+
195
+ ## Layout pitfalls (must avoid)
196
+
197
+ Carnival's dense visual language has known traps. Read this list **before** writing a card layout — these are mistakes that have happened in earlier builds and shipped looking broken.
198
+
199
+ 1. **Halftone portrait + side-by-side text in a narrow card → text gets covered.** When pairing a halftone "host portrait" / "artist portrait" with a show / artist name in the same card, **never use a 2-column grid where the content column can collapse below 200 px**. On a 3-up `repeat(3, 1fr)` desktop grid, the content column inside each card is ~150–180 px wide and a 5 rem portrait squeezes the title into overlap. The safe patterns:
200
+ - **Vertical stack** (portrait on top, content below — the portrait sits naturally in the top-left of the card content area because of card padding). This is the default for portrait-paired cards.
201
+ - **Side-by-side ONLY at 2-up or 1-up grids** where the content column has ≥ 250 px to breathe.
202
+ - **Sticker corner** — portrait positioned `absolute; top: 0; right: 0` as a small badge, with text content flowing full-width below.
203
+
204
+ Concretely: if the card is in a `repeat(3, 1fr)` grid and includes a portrait + title pair, the card must use `display: flex; flex-direction: column;` with the portrait at a fixed `width: 4.5rem; height: 4.5rem;` (not `width: 100%`). Halftone squares **never** overlap typography.
205
+
206
+ 2. **Hard-offset shadow on a card next to a viewport edge → shadow gets clipped.** When a card sits flush against the right edge of the page gutter, its `4px 4px 0` shadow extends past the page max-width. Add `padding-right: max(var(--page-gutter), 8px)` to grids that include shadowed cards near the edge, or set the shell to have ≥ 8 px right margin past the card grid.
207
+
208
+ 3. **Duo-tone fill on a card whose content includes both accents → reads as "blended", not "competing".** A mustard tile must never contain an oxblood badge inside it. Pick one accent per tile. (Signature #1 explicitly says this — but it's worth restating here because it gets violated when adding a sticker or a "NEW" badge.)
209
+
210
+ 4. **Marquee scroll that doesn't repeat its content → gap at end of loop.** The marquee must contain the same scrolling text repeated at least twice (or use `aria-hidden` siblings) so the scroll animation reads as continuous, not as a single string sliding off.
211
+
212
+ 5. **Big Shoulders width axis used without `font-variation-settings` → no width variation.** Setting `font-stretch` on Google's Big Shoulders Display does not work — only `font-variation-settings: "wdth" 110` does. If you see a hero that's supposed to be width-110 but renders at width-100, this is the bug.
213
+
214
+ 6. **Section head detached from, or mis-centred over, its body.** The section head (heading + any lede) sits **tight above** the content it names — about `1.25rem` (`--section-head-gap`) — and **shares that content's alignment**. Don't cap the head at a narrow `max-width` / `ch` and then `margin-inline: auto` it: that strands a centred head over a full-width, left-flush grid (the classic accidental mismatch). Left-flush grid → left-flush head. (See the alignment-coherence note in `layout-and-space.md`.)
215
+
216
+ 7. **Newsletter / CTA button not aligned with the input beside it.** When a button sits next to a form input in one row, they must share the **same height, vertical padding, and border width**, and the row must use `align-items: center` — otherwise the button floats above the input's baseline. If a label is stacked above the input it makes that column taller; centre the row on the *control*, not the column (give the field `align-items: center` or pull the label out of the flex row).
217
+
218
+ ## Macrostructure affinity
219
+
220
+ **Carnival loves these.**
221
+
222
+ - **Marquee Hero** — the canonical Carnival opening; large word, scrolling banner under
223
+ - **Type Specimen** — the page IS the type; Big Shoulders Display 96 px+ as content
224
+ - **Manifesto** — short loud declarative statements (works for Carnival's voice too, but louder palette)
225
+ - **Stat-Led** — when the page leads with numbers, Carnival displays them big-and-mustard
226
+ - **Photographic** — image-led with halftone treatments and ornament captions
227
+ - **Bento Grid** (loud variant) — gridded blocks where each tile alternates accent colour
228
+
229
+ ## Macrostructure rejection
230
+
231
+ **Carnival refuses these.**
232
+
233
+ - **Long Document** — too quiet; Carnival doesn't sustain over 1500 words
234
+ - **Letter** — too intimate; Carnival never whispers
235
+ - **Quote-Led** — too pensive
236
+ - **Conversational FAQ** — too soft
237
+ - **Workbench** — too technical; Carnival is poster art, not product spec
238
+
239
+ If the brief would otherwise land in one of these, redirect to Marquee Hero or Manifesto.
240
+
241
+ ## Voice fixtures
242
+
243
+ Sample lines the Carnival voice should *read like*. Short. Loud. Declarative. Caps on the headlines, not on the body.
244
+
245
+ - *"FIVE NEW FLAVORS. ALL TOO HOT."*
246
+ - *"WE PRESS RECORDS. THAT'S IT."*
247
+ - *"NEW SEASON. NEW SHOWS. SAME CHAOS."*
248
+ - *"ONE EP. THREE ARTISTS. NO REGRETS."*
249
+ - *"THE GAME IS PIXEL ART. THE GAME IS HARD."*
250
+
251
+ Body copy is **shorter than other themes**. 1–2 short sentences per paragraph. Paragraphs separated by ornament dividers, not whitespace alone.
252
+
253
+ **Voice rules:**
254
+ - Headlines: ALL CAPS, ≤ 6 words, period at end (not exclamation — the loudness is in the type, not the punctuation).
255
+ - Body: sentence case, short, present-tense.
256
+ - Numerals over words always (*5* not *five*).
257
+ - Never any of: "experience", "journey", "elevate", "curate", "platform", "ecosystem", "transform". These are SaaS-default AI tells; Carnival is independent, not platformed.
258
+
259
+ ## Anti-patterns (theme-specific)
260
+
261
+ - **Never soft shadows.** Carnival is hard-offset (`4px 4px 0`) or no shadow. If you find yourself reaching for `0 8px 24px`, redirect.
262
+ - **Never both accents in one block.** Mustard *or* oxblood per section, never blended.
263
+ - **Never sentence-case headlines.** Headlines are ALL CAPS or `font-variant: all-petite-caps`.
264
+ - **Never light-grey text.** Body text is full-ink. Carnival doesn't do "subtle".
265
+ - **Never long paragraphs.** If a paragraph exceeds 3 sentences, break it with an ornament divider or split into two blocks.
266
+ - **Never neutral CTAs.** Every CTA fills with one of the two accent colours.
267
+ - **Never thin rules.** Rules are 2 px solid in oxblood. Hairlines belong to Boutique / Specimen.
268
+ - **Emoji as decoration is allowed when typographic** (✱ ❋ ◆ are typographic ornaments, not emoji). Smiley-face / heart emoji are still banned per universal anti-patterns.
269
+
270
+ ## How Carnival differs from neighbouring themes
271
+
272
+ | vs | difference |
273
+ |---|---|
274
+ | **Riso** | Riso is risograph print-craft (peach paper, CMYK misregistration on display, cyan + yellow). Carnival is *editorial maximalism* — duo-tone, oxblood-on-pink, decorative ornaments, variable-width type. Different parent tradition. |
275
+ | **Manifesto** | Manifesto is BLACK paper + ALL CAPS red Anton. Carnival is *tinted warm paper* + duo accents + ornaments. Inverted polarity — Manifesto is dark, Carnival is light. |
276
+ | **Brutal** | Brutal is raw graphic-design brutalism (heavy borders, slab type, no ornaments). Carnival is **decorative** — ornaments, layered blocks, variable type, character. |
277
+ | **Sport** | Sport is athletic italic uppercase (Inter Tight italic 700). Carnival is poster-art expressive (Big Shoulders 800 with width axis). Sport feels Nike; Carnival feels Drag City. |
278
+
279
+ ## Test brief expectations
280
+
281
+ Carnival should be a candidate when the brief mentions:
282
+
283
+ - *record label · podcast · comedy · indie · zine · poster · illustrator · games · hot sauce · merch · streetwear · cassette · vinyl · cassettes · live show · zine · brand of one*
284
+ - product categories: music · comedy · games · illustration · hot sauce · merch · skateboards · gig posters
285
+ - emotional tone: *loud, fun, scrappy, chaotic, layered, character-led, decorative*
286
+
287
+ Briefs that say *enterprise / scale / API / B2B / dashboard* never route to Carnival.
288
+
289
+ ## Build hint
290
+
291
+ When emitting a Carnival page, the first 12 lines of CSS should establish the four most important tells:
292
+
293
+ ```css
294
+ body { background: var(--color-paper); color: var(--color-ink); font-family: var(--font-body); }
295
+ h1, h2 { font-family: var(--font-display); font-weight: 800; font-variation-settings: "wdth" 110; letter-spacing: 0.02em; line-height: 0.92; text-transform: uppercase; } /* hero word goes tighter: letter-spacing: -0.005em; line-height: 0.82 */
296
+ .card, .cta { box-shadow: 4px 4px 0 var(--color-ink); border: 2px solid var(--color-ink); }
297
+ .section:nth-of-type(odd) { background: var(--color-accent); color: var(--color-accent-ink); }
298
+ .section:nth-of-type(even) { background: var(--color-accent-2); color: var(--color-paper); }
299
+ .ornament { color: var(--color-accent); }
300
+ .halftone { background-image: radial-gradient(var(--color-ink) 1.5px, transparent 1.5px); background-size: 12px 12px; }
301
+ ```
302
+
303
+ Those rules carry 70 % of the theme's identity. The rest is content fit.