@mohammadhprp/system-prompt 0.11.2 → 0.12.1

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 (92) hide show
  1. package/framework/agents/researcher.md +1 -1
  2. package/framework/mcps/github-mcp/README.md +1 -1
  3. package/framework/mcps/github-mcp/configs/.env.example +1 -1
  4. package/framework/plugins/ponytail/README.md +1 -1
  5. package/framework/plugins/ponytail/capabilities.md +1 -1
  6. package/framework/references/standards/pull-requests.md +1 -1
  7. package/framework/skills/README.md +3 -3
  8. package/framework/skills/diagram-design/SKILL.md +1 -1
  9. package/framework/skills/effective-html/SKILL.md +63 -0
  10. package/framework/skills/effective-html/examples.md +19 -0
  11. package/framework/skills/effective-html/references/charts-and-data.md +32 -0
  12. package/framework/skills/effective-html/references/creative-direction.md +48 -0
  13. package/framework/skills/effective-html/references/design-artifact.md +78 -0
  14. package/framework/skills/effective-html/references/diagrams.md +68 -0
  15. package/framework/skills/effective-html/references/documents-and-presentations.md +28 -0
  16. package/framework/skills/effective-html/references/html-diagram.md +43 -0
  17. package/framework/skills/effective-html/references/html-plan.md +40 -0
  18. package/framework/skills/effective-html/references/html-prototype.md +97 -0
  19. package/framework/skills/effective-html/references/html-wireframe.md +81 -0
  20. package/framework/skills/effective-html/references/html.md +72 -0
  21. package/framework/skills/effective-html/references/interfaces.md +17 -0
  22. package/framework/skills/great-interface/SKILL.md +43 -0
  23. package/framework/skills/great-interface/references/animations.md +205 -0
  24. package/framework/skills/great-interface/references/better-accessibility.md +106 -0
  25. package/framework/skills/great-interface/references/better-colors.md +100 -0
  26. package/framework/skills/great-interface/references/better-interface.md +132 -0
  27. package/framework/skills/great-interface/references/better-layout.md +76 -0
  28. package/framework/skills/great-interface/references/better-typography.md +157 -0
  29. package/framework/skills/great-interface/references/better-ui.md +107 -0
  30. package/framework/skills/great-interface/references/better-writing.md +109 -0
  31. package/framework/skills/great-interface/references/choosing-fonts.md +64 -0
  32. package/framework/skills/great-interface/references/color-formats.md +90 -0
  33. package/framework/skills/great-interface/references/color-usage.md +118 -0
  34. package/framework/skills/great-interface/references/contrast.md +79 -0
  35. package/framework/skills/great-interface/references/css-cheat-sheet.md +65 -0
  36. package/framework/skills/great-interface/references/details-and-accessibility.md +119 -0
  37. package/framework/skills/great-interface/references/enter-exit.md +147 -0
  38. package/framework/skills/great-interface/references/explain-interface.md +126 -0
  39. package/framework/skills/great-interface/references/find-the-effect.md +94 -0
  40. package/framework/skills/great-interface/references/focus-and-keyboard.md +131 -0
  41. package/framework/skills/great-interface/references/forms.md +84 -0
  42. package/framework/skills/great-interface/references/from-an-image.md +55 -0
  43. package/framework/skills/great-interface/references/grouping-and-alignment.md +123 -0
  44. package/framework/skills/great-interface/references/hit-areas.md +94 -0
  45. package/framework/skills/great-interface/references/icon-transitions.md +102 -0
  46. package/framework/skills/great-interface/references/icons.md +110 -0
  47. package/framework/skills/great-interface/references/interface-review.md +148 -0
  48. package/framework/skills/great-interface/references/motion-and-zoom.md +79 -0
  49. package/framework/skills/great-interface/references/no-browser.md +73 -0
  50. package/framework/skills/great-interface/references/palette-generation.md +104 -0
  51. package/framework/skills/great-interface/references/palette-structure.md +76 -0
  52. package/framework/skills/great-interface/references/performance.md +88 -0
  53. package/framework/skills/great-interface/references/picker.md +76 -0
  54. package/framework/skills/great-interface/references/read-the-system.md +178 -0
  55. package/framework/skills/great-interface/references/removed-signals.md +38 -0
  56. package/framework/skills/great-interface/references/review-format.md +46 -0
  57. package/framework/skills/great-interface/references/scope-resolution.md +88 -0
  58. package/framework/skills/great-interface/references/screen-readers.md +101 -0
  59. package/framework/skills/great-interface/references/semantics-and-aria.md +84 -0
  60. package/framework/skills/great-interface/references/spacing-and-adaptivity.md +159 -0
  61. package/framework/skills/great-interface/references/spacing-and-sizing.md +121 -0
  62. package/framework/skills/great-interface/references/surfaces.md +219 -0
  63. package/framework/skills/great-interface/references/token-naming.md +97 -0
  64. package/framework/skills/great-interface/references/variable-fonts-and-opentype.md +105 -0
  65. package/framework/skills/great-interface/references/variant.md +104 -0
  66. package/framework/skills/great-interface/references/wrapping-and-punctuation.md +55 -0
  67. package/framework/skills/taste/SKILL.md +47 -0
  68. package/framework/skills/taste/examples.md +73 -0
  69. package/framework/skills/taste/references/brandkit.md +793 -0
  70. package/framework/skills/taste/references/design-taste-frontend-v1.md +221 -0
  71. package/framework/skills/taste/references/design-taste-frontend.md +1201 -0
  72. package/framework/skills/taste/references/full-output-enforcement.md +44 -0
  73. package/framework/skills/taste/references/gpt-taste.md +69 -0
  74. package/framework/skills/taste/references/high-end-visual-design.md +93 -0
  75. package/framework/skills/taste/references/image-to-code.md +1223 -0
  76. package/framework/skills/taste/references/imagegen-frontend-mobile.md +1460 -0
  77. package/framework/skills/taste/references/imagegen-frontend-web.md +982 -0
  78. package/framework/skills/taste/references/industrial-brutalist-ui.md +87 -0
  79. package/framework/skills/taste/references/minimalist-ui.md +80 -0
  80. package/framework/skills/taste/references/redesign-existing-projects.md +173 -0
  81. package/framework/skills/taste/references/stitch-design-taste-design.md +121 -0
  82. package/framework/skills/taste/references/stitch-design-taste.md +179 -0
  83. package/package.json +1 -1
  84. package/src/agent-configs.js +6 -2
  85. package/src/catalog.js +3 -2
  86. package/src/cli.js +82 -2
  87. package/src/doctor.js +84 -0
  88. package/src/installer.js +337 -154
  89. package/framework/skills/brand-guidelines/LICENSE.txt +0 -202
  90. package/framework/skills/brand-guidelines/SKILL.md +0 -73
  91. package/framework/skills/lavish/SKILL.md +0 -67
  92. package/framework/skills/lavish/examples.md +0 -31
@@ -0,0 +1,109 @@
1
+ # Interface writing
2
+
3
+ Clear and brief beats clever; consistent beats varied. The best error message is the interaction redesigned so the error cannot happen.
4
+
5
+ How copy renders (capitalization via `text-transform`, truncation, smart punctuation) belongs to `better-typography`. Error markup and announcements (`aria-invalid`, live regions) belong to `better-accessibility`. Room for translated strings belongs to `better-layout`.
6
+
7
+ ## Recon the existing voice
8
+
9
+ Before writing or reviewing, read the copy nearby. Note the product's terminology, its localization conventions and any voice or content style guide.
10
+
11
+ A deliberate brand voice is not a defect. Raise a departure from plain language only when it creates inconsistency, ambiguity, translation risk, or a tone the stakes don't support.
12
+
13
+ ## One voice, flexible tone
14
+
15
+ The product has one voice and its existing copy establishes it. A local edit does not get to invent a new one. Keep terms consistent: if it's "Archive" in the menu, it isn't "Move to storage" in the toast. Tone flexes with the stakes:
16
+
17
+ | Context | Tone |
18
+ | --- | --- |
19
+ | Success, onboarding, empty states | Warm, can be light |
20
+ | Routine actions, settings | Neutral, minimal |
21
+ | Errors, destructive confirmations | Calm, plain, zero playfulness |
22
+ | Data loss, security | Serious, explicit |
23
+
24
+ ## Address the reader directly
25
+
26
+ In instructional copy, write "you", not "the user". In errors, "we" invites ambiguity and reads as deflection, so prefer "Unable to load content" over "We're having trouble loading this content". An established first-person voice can stay in low-stakes copy where it still reads clearly.
27
+
28
+ Use possessives sparingly: "Favorites" beats "Your Favorites". Hold one perspective throughout a flow.
29
+
30
+ ## Plain words over clever ones
31
+
32
+ Choose words a tired reader gets on the first pass, and delete every word that does no work. No idioms, no colloquialisms, no humor that won't translate.
33
+
34
+ Skip unnecessary gender: "Subscribers can post recipes", not "each subscriber can post his or her recipes". Match the input device: "tap" on touch, "click" with a pointer, "select" when both are possible.
35
+
36
+ Never assemble a sentence from fragments around a variable (`"You have " + n + " new messages"`), because word order changes per language. Use a full templated string with proper pluralization.
37
+
38
+ ## Verb-first buttons
39
+
40
+ A button label starts with a verb naming the action: "Send", "Save draft", "Delete project". Never "OK!", "Let's go!", or a bare "Yes" and "No" on a consequential action.
41
+
42
+ A confirmation button repeats the consequence, so the dialog is answerable without reading the body. "Delete this project?" offers `Delete project` and `Cancel`.
43
+
44
+ ## Consistent flow vocabulary
45
+
46
+ A multi-step flow uses one vocabulary throughout: "Get started" to enter, "Continue" or "Next" (pick one) to advance, "Done" to finish. Alternating synonyms makes users wonder whether the buttons do different things.
47
+
48
+ ## Links describe their destination
49
+
50
+ Link text has to make sense out of context, because screen-reader users navigate by a list of the page's links. Write "Read the billing docs". "Click here" fails this and the device-verb rule at once.
51
+
52
+ A bare "Learn more" breaks down as soon as two appear on one page. Suffix each one: "Learn more about exports".
53
+
54
+ ## One capitalization policy
55
+
56
+ Pick title case or sentence case per element type, then apply it to every instance of that type. Sentence case is the safer default. It is calmer, has no per-word rules to remember and localizes cleanly. "Save Changes" beside "Discard changes" reads as sloppiness.
57
+
58
+ ## Settings describe the ON state
59
+
60
+ Label a toggle for what happens when it is on. "Send read receipts" lets users infer the off state; the negative ("Don't send read receipts") turns the toggle into a double negative.
61
+
62
+ Link straight to a referenced setting rather than describing the path to it: a "Notification settings" link, not "Go to Settings > Notifications > Email".
63
+
64
+ ## Errors say how to fix, next to where it broke
65
+
66
+ An error is an instruction, and it belongs beside the field that failed:
67
+
68
+ | Bad | Good |
69
+ | --- | --- |
70
+ | That password is too short | Choose a password with at least 8 characters |
71
+ | Invalid name | Use only letters for your name |
72
+ | Oops! Something went wrong. | Unable to save. Check your connection and try again. |
73
+
74
+ No blame, no "oops", no exclamation marks. Phrase hints positively ("Use only letters", not "Don't use numbers or symbols") and show them before the mistake, not after. When the same error keeps firing, redesign the interaction instead of rewording it.
75
+
76
+ ## Empty states point forward
77
+
78
+ An empty state says what this place is, how to fill it and offers one clear next action:
79
+
80
+ ```html
81
+ <!-- Bad: a shrug -->
82
+ <p>No results.</p>
83
+
84
+ <!-- Good: orientation plus a next step -->
85
+ <p class="font-medium">No projects yet</p>
86
+ <p class="text-sm text-zinc-500">Projects keep your tasks and files together.</p>
87
+ <button class="mt-4">Create a project</button>
88
+ ```
89
+
90
+ A search or filter empty state names the query and offers an exit: "No results for 'quarterly'. Clear filters". Never park persistent information in an empty state. It disappears the moment content exists.
91
+
92
+ ## Placeholders are examples, not labels
93
+
94
+ A placeholder shows the expected format: `name@example.com`, `DD/MM/YYYY`. It vanishes on input, so it is never the only label. Every field keeps a visible one.
95
+
96
+ ## Reporting
97
+
98
+ **Severity.** `HIGH` misleads the user or hides how to recover from an error. `MEDIUM` breaks voice, terminology, or capitalization consistency. `LOW` is isolated wording polish.
99
+
100
+ **Verification.** Source alone is enough here. Check every label against the action it invokes, every error for a stated fix and terminology against the copy around it. No browser check is required.
101
+
102
+ **Format.** Group findings under the principle each violates, ordered by severity, one row per root cause listing every location it appears in:
103
+
104
+ | Severity | Location | Before | After | Why |
105
+ | --- | --- | --- | --- | --- |
106
+
107
+ `Location` is `path/to/file:line`. `Why` names the principle and the user impact.
108
+
109
+ End with `Block` when any `HIGH` remains, `Approve` otherwise, leaving the rest in the table as work to do. Never `Approve` coverage you did not inspect. With nothing to report, state "No actionable writing findings" and report verification.
@@ -0,0 +1,64 @@
1
+ # Choosing fonts
2
+
3
+ Choosing a typeface, the right file format and why fonts look the way they do.
4
+
5
+ ## Choosing a typeface
6
+
7
+ Font families set the tone before the specific font does.
8
+
9
+ | Category | Traits | Use for |
10
+ | --- | --- | --- |
11
+ | Serif | Small strokes at the ends of letters guide the eye along a line | Long passages, editorial reading |
12
+ | Sans-serif | Clean, even shapes that stay crisp at small sizes | Default for most interfaces (Helvetica, Inter, Geist) |
13
+ | Monospace | Every glyph the same width so columns line up | Code, tables, tabular data |
14
+ | Display | Drawn for large headlines | Marketing headlines, hero text |
15
+ | Script | Mimics handwriting | Rare, decorative moments |
16
+
17
+ CSS exposes `cursive` and `fantasy` keywords for the last two categories.
18
+
19
+ "Display" in a font's name does not make it a display font. SF Pro and Heldane ship a `Display` variant for large sizes and a `Text` variant for small ones. Use the variant matching the size you are setting.
20
+
21
+ ### Rules
22
+
23
+ - Fewer fonts is usually better. Rarely use more than three. Marketing pages can be more expressive than apps.
24
+ - The same applies to sizes and weights. They define hierarchy, and overusing them hurts readability fast.
25
+ - Pair for contrast, not similarity. A serif headline over a sans body reads as a deliberate display and reading split; two near-identical sans-serifs read as a mistake.
26
+ - Thin weights are display-only. Below `18px` stay at weight `400`+, because Ultralight, Thin and Light (`100`–`300`) strokes disappear at text sizes and on low-DPI screens. Reserve them for `28px`+ display text, and check even there that they hold against the background.
27
+
28
+ ## Font family scope
29
+
30
+ Applying or reviewing typography never requires a new typeface. Use the product's type system unless the task asks for a type change, and never introduce a paid or proprietary face to satisfy a checklist. Rendering details such as font smoothing, wrapping and tabular numbers do not override the project's font family.
31
+
32
+ When a type change is asked for, two routes. The system stack gives a native macOS and iOS feel. A commercial face such as Helvetica Now is a brand decision and still needs a fallback stack.
33
+
34
+ ```css
35
+ /* System-native macOS/iOS feel */
36
+ html {
37
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
38
+ }
39
+
40
+ /* Commercial brand face with safe fallbacks */
41
+ html {
42
+ font-family: "Helvetica Now", "Helvetica Neue", Arial, sans-serif;
43
+ }
44
+ ```
45
+
46
+ ## Formats
47
+
48
+ | Format | Notes |
49
+ | --- | --- |
50
+ | `.woff2` | Brotli compression, broadly supported. Use this on the web. |
51
+ | `.woff` | Older compression. Fallback only for very old browsers. |
52
+ | `.ttf` / `.otf` | Raw formats, no web compression, larger files. Desktop only unless there is no other option. |
53
+
54
+ ## Anatomy of a typeface
55
+
56
+ | Term | Meaning |
57
+ | --- | --- |
58
+ | x-height | Height of a lowercase `x` |
59
+ | Cap height | Height of uppercase letters |
60
+ | Baseline | The invisible line letters sit on |
61
+ | Ascender | Part of a letter rising above the x-height |
62
+ | Descender | Part dropping below the baseline |
63
+
64
+ These measurements are why two fonts at the same `font-size` look like different sizes. A large x-height looks bigger.
@@ -0,0 +1,90 @@
1
+ # Color formats
2
+
3
+ Which notation to write colors in, how to convert between them and what happens at the edges of a display's gamut. Every other rule in this skill is stated perceptually and holds whatever notation you write.
4
+
5
+ ## Choosing a notation
6
+
7
+ | Notation | Good for | Weakness |
8
+ | --- | --- | --- |
9
+ | Hex | Universal support, compact, what design tools hand you | Opaque. No channel is readable or editable by hand |
10
+ | `rgb()` | Same reach as hex, readable alpha | Channels do not correspond to anything a designer thinks about |
11
+ | `hsl()` | Channels look like design controls | Its lightness is not perceptual and its hue drifts; a ramp built by varying lightness bunches at one end and shifts hue |
12
+ | `oklch()` | Perceptually uniform lightness, stable hue, predictable ramps | Baseline 2023, so very old browser matrices need a fallback |
13
+
14
+ **Match whatever the project already uses.** Notation is not a defect: a project on hex is not doing it wrong.
15
+
16
+ **For a genuinely new color system, `oklch()` is the best default.** Even lightness steps stay even, and a fixed hue stays fixed. See [palette-generation.md](palette-generation.md).
17
+
18
+ ```css
19
+ oklch(L C H) /* lightness 0–1, chroma 0–~0.4, hue 0–360 */
20
+ oklch(L C H / alpha) /* alpha uses a slash, never a comma */
21
+ ```
22
+
23
+ ## Converting
24
+
25
+ Convert when the user asks, when an agreed migration is in scope, or when the project is standardizing on a notation and this value is the straggler. Never convert an isolated value in a project that deliberately uses something else and never because this skill happened to load.
26
+
27
+ When conversion is in scope, change the values and nothing else:
28
+
29
+ - Leave CSS keywords alone: `currentColor`, `inherit`, `transparent`, `initial`, `unset`.
30
+ - Leave gradient functions alone. Convert the color stops inside them; do not touch the interpolation method.
31
+ - Leave colors in third-party configs that expect a specific format.
32
+ - Preserve comments and formatting.
33
+
34
+ ```css
35
+ /* Before */
36
+ color: #3b82f6;
37
+ border: 1px solid rgba(0, 0, 0, 0.1);
38
+
39
+ /* After */
40
+ color: oklch(0.623 0.188 259.815);
41
+ border: 1px solid oklch(0 0 0 / 0.1);
42
+ ```
43
+
44
+ Bulk conversion is a migration, not cleanup. It shifts every rendered color by a rounding margin and touches files nobody asked about, so it has to be the task rather than a side effect.
45
+
46
+ ## Gamut
47
+
48
+ Every sRGB color exists in Display P3, but not the reverse. P3 covers roughly 50% more colors, which matters only for the most saturated values. A color at 60% of maximum vividness looks the same on both.
49
+
50
+ A color more vivid than its display can render gets clipped, and clipping is not graceful. It flattens neighbouring steps into one rendered color, so the top of a ramp can lose its distinctions on an sRGB screen. Maximum vividness varies by hue and lightness. Cyans top out far lower than reds and purples, so a clipping ramp clips at some steps and not others.
51
+
52
+ The fix is to reduce vividness while holding hue and lightness. Generate ramps against sRGB unless the product is display-restricted, and add P3 as an enhancement:
53
+
54
+ ```css
55
+ .accent {
56
+ background: #3b82f6;
57
+ }
58
+
59
+ @media (color-gamut: p3) {
60
+ .accent {
61
+ background: oklch(0.62 0.24 259);
62
+ }
63
+ }
64
+ ```
65
+
66
+ Order matters. The sRGB value comes first so every display gets something, and the P3 rule overrides only where it will render. A P3 color with no fallback is a `HIGH` finding; it does not degrade, it fails.
67
+
68
+ For browser matrices predating `oklch()` support, the same layering works with `@supports`:
69
+
70
+ ```css
71
+ .accent {
72
+ background: #3b82f6;
73
+ }
74
+
75
+ @supports (color: oklch(0 0 0)) {
76
+ .accent {
77
+ background: oklch(0.62 0.19 259);
78
+ }
79
+ }
80
+ ```
81
+
82
+ Check the project's actual browser matrix before adding this. On a modern baseline it is dead weight.
83
+
84
+ ## Modern CSS worth knowing
85
+
86
+ - **`color-mix()`** derives one color from another, as in `color-mix(in oklab, var(--color-accent-solid) 15%, white)` for a tinted background. Useful for states, but keep generated values out of the token layer, since a mixed color cannot be inspected in a design tool.
87
+ - **Relative color syntax** adjusts one channel of an existing color. `oklch(from var(--color-accent-solid) calc(l - 0.1) c h)` darkens the accent by hand. Powerful and easy to overuse, since a token defined by three chained derivations is unreadable.
88
+ - **`light-dark()`** puts both appearances in one declaration. See [palette-generation.md](palette-generation.md).
89
+
90
+ All three compute at render time, so their output cannot be contrast-checked statically. Measure the rendered result.
@@ -0,0 +1,118 @@
1
+ # Color usage
2
+
3
+ Deploying color once the system exists: meaning, emphasis, gradients and appearance variants. For picking values see [palette-generation.md](palette-generation.md), for naming them [token-naming.md](token-naming.md), for checking pairs [contrast.md](contrast.md).
4
+
5
+ ## One color, one meaning
6
+
7
+ Users read a near-miss in hue as a slightly different shade, not as a different color.
8
+
9
+ ```css
10
+ /* Bad: the accent means both "link" and "decorative heading" */
11
+ a { color: #3b82f6; }
12
+ .section-title { color: #4f8ef7; }
13
+
14
+ /* Good: interactive elements own the accent; headings stay neutral */
15
+ a { color: var(--color-accent-text); }
16
+ .section-title { color: var(--color-text-primary); }
17
+ ```
18
+
19
+ The rule runs both ways. A color must not be *absent* where its meaning occurs. If the accent means interactive, an interactive element rendered neutral is just as misleading.
20
+
21
+ Color is never the only carrier of meaning. Pair it with an icon, a label, or a shape. `better-accessibility` owns that requirement.
22
+
23
+ ## Use tokens in their role
24
+
25
+ Apply a semantic token only for the role it names. `--color-text-secondary` is muted foreground text. Use it as a background and every future theme change that assumes the role breaks, because a value that happened to work as both stops working as both.
26
+
27
+ ```css
28
+ /* Bad: separator token repurposed as a text color because it looked right */
29
+ .caption { color: var(--color-border); }
30
+
31
+ /* Bad: text token repurposed as a background */
32
+ .tag { background: var(--color-text-secondary); }
33
+ ```
34
+
35
+ The role inventory in [token-naming.md](token-naming.md) is the list of roles a system needs.
36
+
37
+ ## One colored action per view
38
+
39
+ Preserve an established component hierarchy that communicates emphasis another way. Do not recolor controls merely to impose this recipe.
40
+
41
+ ```html
42
+ <!-- Good: one filled primary action, neutral secondaries -->
43
+ <button class="bg-accent-solid text-white">Save</button>
44
+ <button class="text-neutral-700">Cancel</button>
45
+
46
+ <!-- Bad: every action colored, so nothing is primary -->
47
+ <button class="bg-accent-solid text-white">Save</button>
48
+ <button class="bg-accent-solid text-white">Duplicate</button>
49
+ <button class="bg-accent-solid text-white">Export</button>
50
+ ```
51
+
52
+ Selected states may use the accent on the glyph and label. An active tab or a checked segment is state, not emphasis.
53
+
54
+ ## Gradients
55
+
56
+ **The interpolation space is a look, not a correctness setting.** Three are worth knowing, and the difference between them is most visible in the middle of the gradient:
57
+
58
+ ```css
59
+ /* sRGB: the default and the classic. Midpoint darkens and mutes. */
60
+ background: linear-gradient(#3b82f6, #ec4899);
61
+
62
+ /* oklab: even brightness across the transition. The best default. */
63
+ background: linear-gradient(in oklab, #3b82f6, #ec4899);
64
+
65
+ /* oklch: travels around the hue wheel, staying vivid throughout. */
66
+ background: linear-gradient(in oklch, #3b82f6, #ec4899);
67
+ ```
68
+
69
+ `oklab` and sRGB are **rectangular**, interpolating in a straight line through the color space. `oklch` is **polar**, interpolating the hue angle, so it arcs around the wheel through every hue between the stops. That is why it stays saturated and why it can produce hues nobody asked for. A blue-to-pink gradient routes through purple, which is either the look or a surprise.
70
+
71
+ **The gray dead zone is a rectangular-space problem.** Two hues on opposite sides of the wheel sit either side of the neutral axis. A straight line between them passes near gray, and the middle goes lifeless. Either switch to a polar space, which routes around the axis, or add a third stop between the two and keep the space you have.
72
+
73
+ With a polar space you also control which way it goes around:
74
+
75
+ ```css
76
+ /* The short way round, usually what you want */
77
+ background: linear-gradient(in oklch shorter hue, #3b82f6, #ec4899);
78
+
79
+ /* The long way, sweeps most of the spectrum */
80
+ background: linear-gradient(in oklch longer hue, #3b82f6, #ec4899);
81
+ ```
82
+
83
+ **Banding shows up on large areas.** A gradient spanning a hero with little contrast between its stops steps visibly on 8-bit displays. Widen the contrast, shrink the area, or overlay a subtle noise texture.
84
+
85
+ **Keep text off gradients where you can.** Contrast varies continuously across one, so a single measurement does not describe it. Where text must sit on a gradient, measure the worst region rather than the average, or put a scrim behind it.
86
+
87
+ ## Color across cultures
88
+
89
+ Color meaning is not universal. Where a color is load-bearing in finance, status, or alerts, verify the meaning holds in every locale you ship to.
90
+
91
+ | Color | Common Western reading | Elsewhere |
92
+ | --- | --- | --- |
93
+ | Red | Danger, loss, errors | Luck, prosperity; **gains** in Chinese financial UIs |
94
+ | Green | Success, gains, go | Losses in Chinese financial UIs |
95
+ | White | Purity, cleanliness | Mourning in parts of East Asia |
96
+ | Gold | Premium, luxury | Religious significance in some regions |
97
+
98
+ The classic case is stock tickers, which show gains in green for English locales and red for Chinese ones. Where the product ships to such markets, make gain and loss per-locale tokens rather than hardcoded values.
99
+
100
+ ## Light, dark and increased contrast
101
+
102
+ Every custom color needs a light and a dark variant, derived per [palette-generation.md](palette-generation.md). Beyond that, users who enable increased contrast expect visibly stronger differentiation:
103
+
104
+ ```css
105
+ :root {
106
+ --color-accent-solid: #3b82f6;
107
+ }
108
+
109
+ @media (prefers-color-scheme: dark) {
110
+ :root { --color-accent-solid: #60a5fa; }
111
+ }
112
+
113
+ @media (prefers-contrast: more) {
114
+ :root { --color-accent-solid: #1d4ed8; }
115
+ }
116
+ ```
117
+
118
+ The increased-contrast variant widens the foreground/background gap by at least 15 points of perceived lightness over the default. Re-verify against APCA's preferred thresholds, Lc 90 body and Lc 75 non-body. Widening the gap without remeasuring is not fixing it.
@@ -0,0 +1,79 @@
1
+ # Contrast
2
+
3
+ Contrast is measured between a **foreground color**, meaning text, an icon, or a UI element, and the **background color** it actually renders against, usually the nearest ancestor that paints one. Identify that background first. Measuring against the page background when the element sits on a card gives the wrong answer.
4
+
5
+ **Report, don't repaint.** When a check fails, report the pair, its measured value and the threshold it misses, and leave the colors unchanged. They are a design decision. Apply the fix below only when asked.
6
+
7
+ `better-accessibility` decides when contrast is required and whether a given pair must pass. This file covers measuring the pair and, on request, changing it.
8
+
9
+ ## APCA thresholds (recommended)
10
+
11
+ APCA (Accessible Perceptual Contrast Algorithm) models perceived contrast more accurately than WCAG 2 and is the better default for design decisions. Lc (Lightness Contrast) measures perceived contrast between foreground and background. These levels simplify APCA's full font-size and weight lookup table:
12
+
13
+ | Content type | Minimum | Preferred |
14
+ | --- | --- | --- |
15
+ | Body text (columns or blocks of text) | Lc 75 | Lc 90 |
16
+ | Non-body text (labels, headlines) | Lc 60 | Lc 75 |
17
+ | Large text (≥36px) | Lc 45 | Lc 60 |
18
+ | UI components | Lc 30 | n/a |
19
+
20
+ Lc 30 is also APCA's minimum for disabled and placeholder text. The floor for a non-text element to be discernible at all is Lc 15.
21
+
22
+ Lc is signed: positive means dark text on a light background, negative means light text on a dark background. Compare the absolute value against the threshold.
23
+
24
+ ## WCAG 2 thresholds (for legal compliance)
25
+
26
+ WCAG 2 is still required for formal WCAG 2.x conformance claims. Its luminance ratio is both too strict and too lenient depending on the pair, but it has the legal standing.
27
+
28
+ | Content type | AA | AAA |
29
+ | --- | --- | --- |
30
+ | Normal text (<24px / <18.5px bold) | 4.5:1 | 7:1 |
31
+ | Large text (≥24px / ≥18.5px bold) | 3:1 | 4.5:1 |
32
+ | UI components and graphical objects | 3:1 | n/a |
33
+
34
+ WCAG defines large text in points: 18pt ≈ `24px`, 14pt bold ≈ `18.5px`.
35
+
36
+ When a project must claim WCAG conformance, WCAG is the gate and APCA is the tiebreaker for anything above it.
37
+
38
+ ## Fixing a failing pair (on request)
39
+
40
+ **Change lightness first.** It is the channel contrast responds to. Hue and saturation move the measured value far less, so fixing contrast by changing hue is wasted effort.
41
+
42
+ Move the foreground away from the background in perceived lightness, holding hue and saturation, then remeasure. Keeping hue fixed is what stops a contrast fix becoming a palette change.
43
+
44
+ ```css
45
+ /* Failing: text too close to its background in lightness (Lc ≈ 50) */
46
+ color: #7d93b0;
47
+ background: #eef2f7;
48
+
49
+ /* Fixed: darker text, same hue (Lc ≈ 90) */
50
+ color: #2b3a4f;
51
+ background: #eef2f7;
52
+ ```
53
+
54
+ Two constraints on the fix:
55
+
56
+ - **Mid-lightness backgrounds cap what is achievable.** On a background near 75% perceived lightness, even pure black text reaches only about Lc 60. Body text needs a background near one extreme, so a mid-range background is the thing that has to change.
57
+ - **Pushing lightness can push the color out of gamut.** Reduce saturation as needed to keep it renderable. See [color-formats.md](color-formats.md).
58
+
59
+ Always remeasure after changing a value. Do not assume a fix landed.
60
+
61
+ ## Quick approximations
62
+
63
+ Useful for a first pass and approximations. Verify by measuring before reporting a result.
64
+
65
+ For body text targeting |Lc| ≥ 75:
66
+
67
+ - **Light background (above ~90% perceived lightness):** foreground below ~35%.
68
+ - **Dark background (below ~25% perceived lightness):** foreground above ~90%.
69
+
70
+ The gap is asymmetric because APCA is polarity-aware. Mirrored pairs do not score identically, which is why a pair passing in light mode can fail in dark.
71
+
72
+ **Light or dark background?** The crossover is around 73% perceived lightness. Above it use dark text; at or below it light text scores higher. That is higher than intuition suggests. Between roughly 60% and 73% the background already looks light, yet white text still measures meaningfully better than black.
73
+
74
+ ## What to check
75
+
76
+ - **Every pair, in every appearance.** A pair passing in light mode can fail in dark. The palettes are not mirror images.
77
+ - **Translucent surfaces.** A color on a `backdrop-filter` header or an overlay shifts with whatever scrolls behind it. Test against the lightest and darkest content it can sit over, or make the surface opaque enough that the shift cannot break the pair.
78
+ - **Computed colors.** `color-mix()`, relative color syntax and opacity modifiers resolve at render time; measure the rendered result, not the declaration.
79
+ - **Text over images.** There is no single background color. Measure the worst region, or guarantee one with a scrim.
@@ -0,0 +1,65 @@
1
+ # CSS cheat sheet
2
+
3
+ One-line lookup for every typography CSS declaration covered by this skill, with the Tailwind 4 equivalent. Where no utility exists, the arbitrary-value form is shown. Pick the column that matches the project: the declaration in plain CSS, CSS Modules, styled-components or StyleX codebases, the utility in Tailwind codebases.
4
+
5
+ ## Font
6
+
7
+ | Declaration | What it does | Tailwind |
8
+ | --- | --- | --- |
9
+ | `font-family: sans-serif` | The sans family | `font-sans` |
10
+ | `font-family: serif` | The serif family | `font-serif` |
11
+ | `font-family: monospace` | The monospace family | `font-mono` |
12
+ | `font-size` | Size from the type scale | `text-*` |
13
+ | `font-weight` | Any value from 1 to 1000 | `font-*` |
14
+ | `font-style: italic` | Switch to italic style | `italic` |
15
+ | `-webkit-font-smoothing` + `-moz-osx-font-smoothing` | Smooth macOS font rendering; apply once at the root | `antialiased` |
16
+ | `font-synthesis: none` | Disable all synthesized forms after verifying fallbacks and emphasis | `[font-synthesis:none]` |
17
+ | `font-feature-settings` | Toggle OpenType features | `[font-feature-settings:"ss01"]` |
18
+ | `font-variation-settings` | Tune variable font axes | `[font-variation-settings:"GRAD"_80]` |
19
+ | `font-optical-sizing` | Adjust details per size | `[font-optical-sizing:auto]` |
20
+ | `font-variant-caps` | Real small capitals | `[font-variant-caps:small-caps]` |
21
+ | `font-variant-position` | Real super and subscripts | `[font-variant-position:super]` |
22
+ | `font-variant-numeric: tabular-nums` | Equal-width digits | `tabular-nums` |
23
+ | `font-variant-numeric: slashed-zero` | Tell 0 from O | `slashed-zero` |
24
+
25
+ ## Spacing and layout
26
+
27
+ | Declaration | What it does | Tailwind |
28
+ | --- | --- | --- |
29
+ | `letter-spacing` | Space between letters | `tracking-*` |
30
+ | `line-height` | Space between lines | `leading-*` |
31
+ | `font-kerning` | Kerning on or off | `[font-kerning:none]` |
32
+ | `text-box: trim-both` | Trim space above and below | `[text-box:trim-both_cap_alphabetic]` |
33
+ | `max-width` on text columns | Cap at ~60–75 characters per line | `max-w-xl` / `max-w-2xl` / `max-w-[65ch]` |
34
+ | `text-align` | Where lines start and end | `text-start` / `text-center` |
35
+
36
+ ## Wrapping and overflow
37
+
38
+ | Declaration | What it does | Tailwind |
39
+ | --- | --- | --- |
40
+ | `text-wrap: balance` | Even out heading lines | `text-balance` |
41
+ | `text-wrap: pretty` | Avoid orphaned words | `text-pretty` |
42
+ | `text-overflow: ellipsis` | Ellipsis for clipped text | `truncate` |
43
+ | `line-clamp` | Cut off after N lines | `line-clamp-*` |
44
+ | `overflow-wrap: break-word` | Break long strings | `break-words` |
45
+ | `white-space: nowrap` | Stop wrapping | `whitespace-nowrap` |
46
+ | `text-transform` | Change the casing | `uppercase` / `capitalize` |
47
+
48
+ ## Decoration and interaction
49
+
50
+ | Declaration | What it does | Tailwind |
51
+ | --- | --- | --- |
52
+ | `text-decoration-line: underline` | Draw an underline | `underline` |
53
+ | `text-decoration-color` | Underline color | `decoration-*` |
54
+ | `text-decoration-thickness` | Underline thickness | `decoration-1` / `decoration-2` |
55
+ | `text-underline-offset` | Push the line down | `underline-offset-*` |
56
+ | `text-underline-position: from-font` | Underline position from the font | `[text-underline-position:from-font]` |
57
+ | `text-decoration-style` | Dotted, dashed or wavy | `decoration-dotted` / `decoration-wavy` |
58
+ | `text-decoration-thickness: from-font` | Underline set by the font | `decoration-from-font` |
59
+ | `text-decoration-skip-ink` | Gaps around descenders | `[text-decoration-skip-ink:auto]` |
60
+ | `caret-color` | Tint the text cursor | `caret-*` |
61
+ | `user-select: none` | Suppress selection on a verified drag/gesture conflict only | `select-none` |
62
+ | `text-shadow` | Shadow behind the letters | `text-shadow-*` |
63
+ | `-webkit-text-stroke` | Outline the letters | `[-webkit-text-stroke:1px_black]` |
64
+ | `background-clip: text` | Clip a background to the letters | `bg-clip-text` |
65
+ | `initial-letter` | Size a drop cap | `[initial-letter:3]` |
@@ -0,0 +1,119 @@
1
+ # Details and accessibility
2
+
3
+ Underlines, selection, forms, decorative text and the floors that keep everything readable.
4
+
5
+ ## Underlines
6
+
7
+ Default underline position is browser-determined, sometimes too close, sometimes cutting through descenders, sometimes too thin. Pull position and thickness from the font's own metrics:
8
+
9
+ ```css
10
+ a {
11
+ text-underline-position: from-font;
12
+ text-decoration-thickness: from-font;
13
+ }
14
+ ```
15
+
16
+ A dotted underline on an abbreviation:
17
+
18
+ ```css
19
+ abbr {
20
+ text-decoration: underline dotted;
21
+ }
22
+ ```
23
+
24
+ Or tune manually:
25
+
26
+ ```css
27
+ a {
28
+ text-decoration-thickness: 1px;
29
+ text-underline-offset: 3px;
30
+ text-decoration-skip-ink: auto;
31
+ text-decoration-color: var(--color-gray-1000);
32
+ transition: text-decoration-color 200ms ease-out;
33
+ }
34
+
35
+ a:hover {
36
+ text-decoration-color: var(--color-gray-1200);
37
+ }
38
+ ```
39
+
40
+ Animate the custom element however the effect requires.
41
+
42
+ ## Selection
43
+
44
+ - `::target-text` styles the phrase a shared link scrolls to.
45
+ - The Custom Highlight API styles ranges you pick yourself, like search matches, without extra markup.
46
+
47
+ ## Forms and editable text
48
+
49
+ - `::placeholder` styles the hint in an empty field.
50
+ - `caret-color` colors the blinking insertion bar. Color is about as far as caret styling goes; a fully custom caret is hard to build and rarely worth it.
51
+
52
+ ### iOS input zoom
53
+
54
+ This is an accessibility feature: `16px` is the web default, and Safari treats smaller as too hard to read while typing.
55
+
56
+ The two fixes differ in what they do to the design, not in correctness.
57
+
58
+ **Size up on mobile.** The input renders at `16px` on small screens and drops to the design size from the `sm` breakpoint up. Nothing to compensate, but the mobile input no longer matches the desktop one.
59
+
60
+ ```tsx
61
+ <input className="text-base sm:text-sm" type="email" />
62
+ ```
63
+
64
+ **Scale the text down.** Keep `font-size` at `16px` so Safari never zooms, then render at the intended size with a transform. The design survives at every viewport, at the cost of two compensating calcs. Widen the element by the inverse of the scale so it still fills its container once shrunk, and divide `line-height` by the same factor so the intended leading survives. `origin-left` pins the text to the start edge, `origin-right` under RTL. Above the breakpoint, drop the transform and set the real size.
65
+
66
+ ```tsx
67
+ // 13px rendered from a 16px font-size: 13 / 16 = 0.8125
68
+ <div className="flex h-10 items-center rounded-[10px] bg-gray-300 px-2.5">
69
+ <input
70
+ className="h-full w-[calc(100%/0.8125)] origin-left scale-[0.8125] bg-transparent text-base leading-[calc(1.125/0.8125)] outline-none sm:w-full sm:scale-100 sm:text-[13px]"
71
+ type="email"
72
+ />
73
+ </div>
74
+ ```
75
+
76
+ The transform shrinks the whole box, not only the glyphs, so let a wrapper draw the field's surface and keep the input transparent. A background, border, or ring on the scaled element shrinks with the text and misses the intended hit area.
77
+
78
+ ## Decorative text
79
+
80
+ | Property | Effect |
81
+ | --- | --- |
82
+ | `::first-letter` | Drop cap, widely supported |
83
+ | `::first-line` | Styles only the first line |
84
+ | `initial-letter` | Sizes the drop cap; limited support, no Firefox yet |
85
+ | `background-clip: text` | Clips a background or gradient to the letter shapes |
86
+ | `-webkit-text-stroke` | Outlines the letters; works across modern browsers despite the prefix |
87
+ | `text-shadow` | Like `box-shadow` but follows the character shapes |
88
+
89
+ A text stroke drawing lines inside the letters is the font. The stroke traces every contour, and variable fonts usually keep overlapping shapes unmerged. Static fonts do not have this issue.
90
+
91
+ ## Sizes
92
+
93
+ Typography must survive the reader changing it: zoom, a larger browser font size, an overridden line height or letter spacing.
94
+
95
+ | Text | Size |
96
+ | --- | --- |
97
+ | Long-form body starting point | Around `16px`, verified in the actual typeface and measure |
98
+ | Inputs and menus starting point | Around `14px` |
99
+ | Captions | `13px` |
100
+ | Floor | Rarely below `12px` |
101
+
102
+ ## Font smoothing
103
+
104
+ Tailwind's `antialiased` sets both properties:
105
+
106
+ ```css
107
+ html {
108
+ -webkit-font-smoothing: antialiased;
109
+ -moz-osx-font-smoothing: grayscale;
110
+ }
111
+ ```
112
+
113
+ ```tsx
114
+ <html lang="en">
115
+ <body class="font-sans antialiased">
116
+ <main>{children}</main>
117
+ </body>
118
+ </html>
119
+ ```