@mohammadhprp/system-prompt 0.12.0 → 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 (65) hide show
  1. package/framework/skills/README.md +2 -2
  2. package/framework/skills/effective-html/SKILL.md +63 -0
  3. package/framework/skills/effective-html/examples.md +19 -0
  4. package/framework/skills/effective-html/references/charts-and-data.md +32 -0
  5. package/framework/skills/effective-html/references/creative-direction.md +48 -0
  6. package/framework/skills/effective-html/references/design-artifact.md +78 -0
  7. package/framework/skills/effective-html/references/diagrams.md +68 -0
  8. package/framework/skills/effective-html/references/documents-and-presentations.md +28 -0
  9. package/framework/skills/effective-html/references/html-diagram.md +43 -0
  10. package/framework/skills/effective-html/references/html-plan.md +40 -0
  11. package/framework/skills/effective-html/references/html-prototype.md +97 -0
  12. package/framework/skills/effective-html/references/html-wireframe.md +81 -0
  13. package/framework/skills/effective-html/references/html.md +72 -0
  14. package/framework/skills/effective-html/references/interfaces.md +17 -0
  15. package/framework/skills/great-interface/SKILL.md +43 -0
  16. package/framework/skills/great-interface/references/animations.md +205 -0
  17. package/framework/skills/great-interface/references/better-accessibility.md +106 -0
  18. package/framework/skills/great-interface/references/better-colors.md +100 -0
  19. package/framework/skills/great-interface/references/better-interface.md +132 -0
  20. package/framework/skills/great-interface/references/better-layout.md +76 -0
  21. package/framework/skills/great-interface/references/better-typography.md +157 -0
  22. package/framework/skills/great-interface/references/better-ui.md +107 -0
  23. package/framework/skills/great-interface/references/better-writing.md +109 -0
  24. package/framework/skills/great-interface/references/choosing-fonts.md +64 -0
  25. package/framework/skills/great-interface/references/color-formats.md +90 -0
  26. package/framework/skills/great-interface/references/color-usage.md +118 -0
  27. package/framework/skills/great-interface/references/contrast.md +79 -0
  28. package/framework/skills/great-interface/references/css-cheat-sheet.md +65 -0
  29. package/framework/skills/great-interface/references/details-and-accessibility.md +119 -0
  30. package/framework/skills/great-interface/references/enter-exit.md +147 -0
  31. package/framework/skills/great-interface/references/explain-interface.md +126 -0
  32. package/framework/skills/great-interface/references/find-the-effect.md +94 -0
  33. package/framework/skills/great-interface/references/focus-and-keyboard.md +131 -0
  34. package/framework/skills/great-interface/references/forms.md +84 -0
  35. package/framework/skills/great-interface/references/from-an-image.md +55 -0
  36. package/framework/skills/great-interface/references/grouping-and-alignment.md +123 -0
  37. package/framework/skills/great-interface/references/hit-areas.md +94 -0
  38. package/framework/skills/great-interface/references/icon-transitions.md +102 -0
  39. package/framework/skills/great-interface/references/icons.md +110 -0
  40. package/framework/skills/great-interface/references/interface-review.md +148 -0
  41. package/framework/skills/great-interface/references/motion-and-zoom.md +79 -0
  42. package/framework/skills/great-interface/references/no-browser.md +73 -0
  43. package/framework/skills/great-interface/references/palette-generation.md +104 -0
  44. package/framework/skills/great-interface/references/palette-structure.md +76 -0
  45. package/framework/skills/great-interface/references/performance.md +88 -0
  46. package/framework/skills/great-interface/references/picker.md +76 -0
  47. package/framework/skills/great-interface/references/read-the-system.md +178 -0
  48. package/framework/skills/great-interface/references/removed-signals.md +38 -0
  49. package/framework/skills/great-interface/references/review-format.md +46 -0
  50. package/framework/skills/great-interface/references/scope-resolution.md +88 -0
  51. package/framework/skills/great-interface/references/screen-readers.md +101 -0
  52. package/framework/skills/great-interface/references/semantics-and-aria.md +84 -0
  53. package/framework/skills/great-interface/references/spacing-and-adaptivity.md +159 -0
  54. package/framework/skills/great-interface/references/spacing-and-sizing.md +121 -0
  55. package/framework/skills/great-interface/references/surfaces.md +219 -0
  56. package/framework/skills/great-interface/references/token-naming.md +97 -0
  57. package/framework/skills/great-interface/references/variable-fonts-and-opentype.md +105 -0
  58. package/framework/skills/great-interface/references/variant.md +104 -0
  59. package/framework/skills/great-interface/references/wrapping-and-punctuation.md +55 -0
  60. package/package.json +1 -1
  61. package/src/catalog.js +2 -2
  62. package/framework/skills/brand-guidelines/LICENSE.txt +0 -202
  63. package/framework/skills/brand-guidelines/SKILL.md +0 -73
  64. package/framework/skills/lavish/SKILL.md +0 -67
  65. package/framework/skills/lavish/examples.md +0 -31
@@ -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
+ ```
@@ -0,0 +1,147 @@
1
+ # Enter and exit animations
2
+
3
+ Staged entrances and the exits that follow them. For interactive state feedback see [animations.md](animations.md); for icon swaps see [icon-transitions.md](icon-transitions.md).
4
+
5
+ ## Enter animations: split and stagger
6
+
7
+ Use this for infrequent staged entrances where sequence communicates hierarchy: the first load of a page hero, a success state, an empty state. Break a large container into semantic chunks and animate each one. Never stagger routine interactions such as row hovers, keystrokes, or repeated tab changes.
8
+
9
+ ### Step by step
10
+
11
+ 1. **Split** into logical groups (title, description, buttons)
12
+ 2. **Stagger** with ~100ms delay between groups
13
+ 3. **For titles**, consider splitting into individual words with ~80ms stagger
14
+ 4. **Combine** `opacity`, `blur` and `translateY` for the enter effect
15
+
16
+ ### Code example
17
+
18
+ ```tsx
19
+ // Motion (Framer Motion): staggered enter
20
+ function PageHeader() {
21
+ return (
22
+ <motion.div
23
+ initial="hidden"
24
+ animate="visible"
25
+ variants={{
26
+ visible: { transition: { staggerChildren: 0.1 } },
27
+ }}
28
+ >
29
+ <motion.h1
30
+ variants={{
31
+ hidden: { opacity: 0, y: 12, filter: "blur(4px)" },
32
+ visible: { opacity: 1, y: 0, filter: "blur(0px)" },
33
+ }}
34
+ >
35
+ Welcome
36
+ </motion.h1>
37
+
38
+ <motion.p
39
+ variants={{
40
+ hidden: { opacity: 0, y: 12, filter: "blur(4px)" },
41
+ visible: { opacity: 1, y: 0, filter: "blur(0px)" },
42
+ }}
43
+ >
44
+ A description of the page.
45
+ </motion.p>
46
+
47
+ <motion.div
48
+ variants={{
49
+ hidden: { opacity: 0, y: 12, filter: "blur(4px)" },
50
+ visible: { opacity: 1, y: 0, filter: "blur(0px)" },
51
+ }}
52
+ >
53
+ <Button>Get started</Button>
54
+ </motion.div>
55
+ </motion.div>
56
+ );
57
+ }
58
+ ```
59
+
60
+ ### CSS-only stagger
61
+
62
+ ```css
63
+ .stagger-item {
64
+ opacity: 0;
65
+ transform: translateY(12px);
66
+ filter: blur(4px);
67
+ animation: fadeInUp 400ms ease-out forwards;
68
+ }
69
+
70
+ .stagger-item:nth-child(1) { animation-delay: 0ms; }
71
+ .stagger-item:nth-child(2) { animation-delay: 100ms; }
72
+ .stagger-item:nth-child(3) { animation-delay: 200ms; }
73
+
74
+ @keyframes fadeInUp {
75
+ to {
76
+ opacity: 1;
77
+ transform: translateY(0);
78
+ filter: blur(0);
79
+ }
80
+ }
81
+ ```
82
+
83
+ ## Exit animations
84
+
85
+ Exits are softer and less attention-grabbing than enters. The user's focus is moving to the next thing, so do not fight for it.
86
+
87
+ ### Subtle exit (recommended)
88
+
89
+ ```tsx
90
+ // Small fixed translateY: indicates direction without drama
91
+ <motion.div
92
+ exit={{
93
+ opacity: 0,
94
+ y: -12,
95
+ filter: "blur(4px)",
96
+ transition: { duration: 0.15, ease: "easeOut" },
97
+ }}
98
+ >
99
+ {content}
100
+ </motion.div>
101
+ ```
102
+
103
+ ### Full exit (when context matters)
104
+
105
+ ```tsx
106
+ // Slide fully out: use when spatial context is important
107
+ // (e.g., a card returning to a list, a drawer closing)
108
+ <motion.div
109
+ exit={{
110
+ opacity: 0,
111
+ x: "-100%",
112
+ transition: { duration: 0.2, ease: "easeOut" },
113
+ }}
114
+ >
115
+ {content}
116
+ </motion.div>
117
+ ```
118
+
119
+ ### Good vs. bad
120
+
121
+ ```css
122
+ /* Good: subtle exit */
123
+ .item-exit {
124
+ opacity: 0;
125
+ transform: translateY(-12px);
126
+ transition: opacity 150ms ease-out, transform 150ms ease-out;
127
+ }
128
+
129
+ /* Bad: dramatic exit that steals focus */
130
+ .item-exit {
131
+ opacity: 0;
132
+ transform: translateY(-100%) scale(0.5);
133
+ transition: all 400ms ease-out;
134
+ }
135
+
136
+ /* Sometimes correct: remove immediately when motion adds no context */
137
+ .item-exit {
138
+ display: none;
139
+ }
140
+ ```
141
+
142
+ **Key points:**
143
+ - Use a small fixed `translateY`, say `-12px`, rather than the full container height
144
+ - Keep some directional movement to indicate where the element went
145
+ - Exit duration should be shorter than enter duration (150ms vs 300ms)
146
+ - Use a subtle exit when it preserves spatial context. Remove immediately when motion adds no information, the interaction repeats frequently, or reduced motion is requested.
147
+