@pugi/cli 1.0.0-alpha.26 → 1.0.0-alpha.260
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.
- package/README.md +19 -8
- package/THIRD_PARTY_NOTICES.md +39 -0
- package/bin/pugi +95 -8
- package/models.json +1 -0
- package/package.json +30 -10
- package/postinstall.mjs +158 -23
- package/release.json +8 -0
- package/skills/a11y/SKILL.md +27 -0
- package/skills/ab-test-analysis/SKILL.md +15 -0
- package/skills/ai-sdk/SKILL.md +27 -0
- package/skills/api-integration/SKILL.md +27 -0
- package/skills/architecture-review/SKILL.md +19 -0
- package/skills/brand-voice/SKILL.md +27 -0
- package/skills/ci-cd-pipeline/SKILL.md +15 -0
- package/skills/code-review/SKILL.md +27 -0
- package/skills/competitor-analysis/SKILL.md +27 -0
- package/skills/content-design/SKILL.md +35 -0
- package/skills/css-tailwind/SKILL.md +39 -0
- package/skills/css-tailwind/quirks.md +86 -0
- package/skills/db-vector/SKILL.md +27 -0
- package/skills/design-radix/SKILL.md +35 -0
- package/skills/design-system/SKILL.md +35 -0
- package/skills/detecting-data-anomalies/SKILL.md +15 -0
- package/skills/docker-deploy/SKILL.md +15 -0
- package/skills/edge-case-finder/SKILL.md +27 -0
- package/skills/fonts/SKILL.md +34 -0
- package/skills/framework-grammy/SKILL.md +27 -0
- package/skills/framework-mastra/SKILL.md +27 -0
- package/skills/framework-nestjs/SKILL.md +27 -0
- package/skills/framework-prisma/SKILL.md +27 -0
- package/skills/funnel-optimization/SKILL.md +19 -0
- package/skills/ha-prototype-web/LICENSE-APACHE-2.0 +201 -0
- package/skills/ha-prototype-web/NOTICE.md +126 -0
- package/skills/ha-prototype-web/SKILL.md +25 -0
- package/skills/icons/SKILL.md +34 -0
- package/skills/landing-templates/SKILL.md +35 -0
- package/skills/market-research/SKILL.md +27 -0
- package/skills/metrics-dashboard/SKILL.md +19 -0
- package/skills/monitoring-setup/SKILL.md +15 -0
- package/skills/motion/SKILL.md +27 -0
- package/skills/nestjs-api/SKILL.md +38 -0
- package/skills/nestjs-api/quirks.md +36 -0
- package/skills/nextjs-page/SKILL.md +27 -0
- package/skills/od-blog-post/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-blog-post/NOTICE.md +126 -0
- package/skills/od-blog-post/SKILL.md +81 -0
- package/skills/od-contact-widget/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-contact-widget/NOTICE.md +126 -0
- package/skills/od-contact-widget/SKILL.md +132 -0
- package/skills/od-craft/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-craft/NOTICE.md +126 -0
- package/skills/od-craft/SKILL.md +58 -0
- package/skills/od-craft/references/README.md +108 -0
- package/skills/od-craft/references/accessibility-baseline.md +203 -0
- package/skills/od-craft/references/animation-discipline.md +156 -0
- package/skills/od-craft/references/anti-ai-slop.md +86 -0
- package/skills/od-craft/references/color.md +90 -0
- package/skills/od-craft/references/form-validation.md +223 -0
- package/skills/od-craft/references/laws-of-ux.md +298 -0
- package/skills/od-craft/references/rtl-and-bidi.md +188 -0
- package/skills/od-craft/references/state-coverage.md +136 -0
- package/skills/od-craft/references/typography-hierarchy-editorial.md +184 -0
- package/skills/od-craft/references/typography-hierarchy.md +167 -0
- package/skills/od-craft/references/typography.md +124 -0
- package/skills/od-critique/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-critique/NOTICE.md +126 -0
- package/skills/od-critique/SKILL.md +259 -0
- package/skills/od-dashboard/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-dashboard/NOTICE.md +126 -0
- package/skills/od-dashboard/SKILL.md +77 -0
- package/skills/od-design-brief/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-design-brief/NOTICE.md +126 -0
- package/skills/od-design-brief/SKILL.md +260 -0
- package/skills/od-docs-page/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-docs-page/NOTICE.md +126 -0
- package/skills/od-docs-page/SKILL.md +81 -0
- package/skills/od-email-marketing/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-email-marketing/NOTICE.md +126 -0
- package/skills/od-email-marketing/SKILL.md +85 -0
- package/skills/od-eng-runbook/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-eng-runbook/NOTICE.md +126 -0
- package/skills/od-eng-runbook/SKILL.md +52 -0
- package/skills/od-faq-page/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-faq-page/NOTICE.md +126 -0
- package/skills/od-faq-page/SKILL.md +113 -0
- package/skills/od-finance-report/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-finance-report/NOTICE.md +126 -0
- package/skills/od-finance-report/SKILL.md +62 -0
- package/skills/od-frontend-design/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-frontend-design/LICENSE.txt +177 -0
- package/skills/od-frontend-design/NOTICE.md +126 -0
- package/skills/od-frontend-design/SKILL.md +86 -0
- package/skills/od-hr-onboarding/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-hr-onboarding/NOTICE.md +126 -0
- package/skills/od-hr-onboarding/SKILL.md +53 -0
- package/skills/od-invoice/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-invoice/NOTICE.md +126 -0
- package/skills/od-invoice/SKILL.md +49 -0
- package/skills/od-kanban-board/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-kanban-board/NOTICE.md +126 -0
- package/skills/od-kanban-board/SKILL.md +51 -0
- package/skills/od-login-flow/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-login-flow/NOTICE.md +126 -0
- package/skills/od-login-flow/SKILL.md +49 -0
- package/skills/od-login-flow/references/checklist.md +19 -0
- package/skills/od-magazine-poster/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-magazine-poster/NOTICE.md +126 -0
- package/skills/od-magazine-poster/SKILL.md +90 -0
- package/skills/od-meeting-notes/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-meeting-notes/NOTICE.md +126 -0
- package/skills/od-meeting-notes/SKILL.md +48 -0
- package/skills/od-mobile-app/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-mobile-app/NOTICE.md +126 -0
- package/skills/od-mobile-app/SKILL.md +103 -0
- package/skills/od-mobile-app/assets/template.html +442 -0
- package/skills/od-mobile-app/references/checklist.md +46 -0
- package/skills/od-mobile-app/references/layouts.md +312 -0
- package/skills/od-mobile-onboarding/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-mobile-onboarding/NOTICE.md +126 -0
- package/skills/od-mobile-onboarding/SKILL.md +54 -0
- package/skills/od-pm-spec/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-pm-spec/NOTICE.md +126 -0
- package/skills/od-pm-spec/SKILL.md +53 -0
- package/skills/od-pricing-page/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-pricing-page/NOTICE.md +126 -0
- package/skills/od-pricing-page/SKILL.md +80 -0
- package/skills/od-redesign-skill/LICENSE +21 -0
- package/skills/od-redesign-skill/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-redesign-skill/NOTICE.md +126 -0
- package/skills/od-redesign-skill/SKILL.md +205 -0
- package/skills/od-reference-design-contract/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-reference-design-contract/NOTICE.md +126 -0
- package/skills/od-reference-design-contract/SKILL.md +131 -0
- package/skills/od-reference-design-contract/references/checklist.md +26 -0
- package/skills/od-saas-landing/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-saas-landing/NOTICE.md +126 -0
- package/skills/od-saas-landing/SKILL.md +125 -0
- package/skills/od-social-carousel/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-social-carousel/NOTICE.md +126 -0
- package/skills/od-social-carousel/SKILL.md +92 -0
- package/skills/od-team-okrs/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-team-okrs/NOTICE.md +126 -0
- package/skills/od-team-okrs/SKILL.md +48 -0
- package/skills/od-waitlist-page/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-waitlist-page/NOTICE.md +126 -0
- package/skills/od-waitlist-page/SKILL.md +229 -0
- package/skills/od-waitlist-page/assets/template.html +457 -0
- package/skills/od-waitlist-page/references/checklist.md +42 -0
- package/skills/od-web-design-guidelines/LICENSE +21 -0
- package/skills/od-web-design-guidelines/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-design-guidelines/NOTICE.md +126 -0
- package/skills/od-web-design-guidelines/SKILL.md +66 -0
- package/skills/od-web-design-guidelines/references/guidelines.md +133 -0
- package/skills/od-web-prototype/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-prototype/NOTICE.md +126 -0
- package/skills/od-web-prototype/SKILL.md +102 -0
- package/skills/od-web-prototype/assets/template.html +348 -0
- package/skills/od-web-prototype/references/checklist.md +47 -0
- package/skills/od-web-prototype/references/layouts.md +247 -0
- package/skills/od-web-prototype-taste-brutalist/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-prototype-taste-brutalist/LICENSE-MIT-Leonxlnx +21 -0
- package/skills/od-web-prototype-taste-brutalist/NOTICE.md +126 -0
- package/skills/od-web-prototype-taste-brutalist/SKILL.md +64 -0
- package/skills/od-web-prototype-taste-editorial/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-prototype-taste-editorial/LICENSE-MIT-Leonxlnx +21 -0
- package/skills/od-web-prototype-taste-editorial/NOTICE.md +126 -0
- package/skills/od-web-prototype-taste-editorial/SKILL.md +61 -0
- package/skills/od-web-prototype-taste-soft/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-web-prototype-taste-soft/LICENSE-MIT-Leonxlnx +21 -0
- package/skills/od-web-prototype-taste-soft/NOTICE.md +126 -0
- package/skills/od-web-prototype-taste-soft/SKILL.md +64 -0
- package/skills/od-webgl-experience/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-webgl-experience/NOTICE.md +126 -0
- package/skills/od-webgl-experience/SKILL.md +81 -0
- package/skills/od-wireframe-annotated/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-wireframe-annotated/NOTICE.md +126 -0
- package/skills/od-wireframe-annotated/SKILL.md +90 -0
- package/skills/od-wireframe-greybox/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-wireframe-greybox/NOTICE.md +126 -0
- package/skills/od-wireframe-greybox/SKILL.md +90 -0
- package/skills/od-wireframe-mobile-flow/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-wireframe-mobile-flow/NOTICE.md +126 -0
- package/skills/od-wireframe-mobile-flow/SKILL.md +96 -0
- package/skills/od-wireframe-sketch/LICENSE-APACHE-2.0 +201 -0
- package/skills/od-wireframe-sketch/NOTICE.md +126 -0
- package/skills/od-wireframe-sketch/SKILL.md +98 -0
- package/skills/orchestrating-multi-agent-systems/SKILL.md +19 -0
- package/skills/preprocessing-data/SKILL.md +15 -0
- package/skills/project-memory-keeper/SKILL.md +19 -0
- package/skills/public-apis/SKILL.md +27 -0
- package/skills/pugi-patterns/SKILL.md +19 -0
- package/skills/react-component/SKILL.md +27 -0
- package/skills/sprint-planning/SKILL.md +19 -0
- package/skills/stack-selection/SKILL.md +15 -0
- package/skills/standup-summary/SKILL.md +15 -0
- package/skills/task-decomposition/SKILL.md +19 -0
- package/skills/team-routing/SKILL.md +15 -0
- package/skills/tech-survey/SKILL.md +15 -0
- package/skills/test-plan/SKILL.md +15 -0
- package/skills/typescript-refactor/SKILL.md +15 -0
- package/skills/ui-components/SKILL.md +35 -0
- package/skills/ux-patterns/SKILL.md +35 -0
- package/skills/web-platform/SKILL.md +27 -0
- package/skills/web-scrape/SKILL.md +27 -0
- package/skills/web-search/SKILL.md +15 -0
- package/skills/webapp-testing/SKILL.md +37 -0
- package/skills/webapp-testing/quirks.md +26 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
<!-- Vendored from nexu-io/open-design@78d5637cb49d (craft/rtl-and-bidi.md), Apache-2.0, Copyright 2026 Open Design contributors. Modified by Pugi: this header line added; the text below is otherwise verbatim. Paths it names (apps/daemon/src/lint-artifact.ts, pnpm lint:craft, od.craft.requires) are upstream's, not Pugi's: the Pugi port of the linter is packages/opencode/src/session/design-lint.ts. See NOTICE.md in the skill root. -->
|
|
2
|
+
|
|
3
|
+
# RTL and bidirectional craft rules
|
|
4
|
+
|
|
5
|
+
Universal rules for right-to-left layout and bidirectional text. The
|
|
6
|
+
active `DESIGN.md` decides brand visual language; this file decides
|
|
7
|
+
how that language behaves when the script reads from the right or
|
|
8
|
+
mixes direction within a line.
|
|
9
|
+
|
|
10
|
+
> Grounded in primary sources: Unicode UAX #9 revision 51 (Sept 2025)
|
|
11
|
+
> + Unicode 17.0, CSS Logical Properties Level 1, HTML Living Standard
|
|
12
|
+
> (`dir`, `<bdi>`), Tailwind v4.0/v4.2 changelogs, W3C alreq,
|
|
13
|
+
> Material 3 RTL guidance, Apple HIG internationalization.
|
|
14
|
+
|
|
15
|
+
## Base direction and language
|
|
16
|
+
|
|
17
|
+
Every full-page RTL artifact needs `<html dir="rtl" lang="ar">` (or
|
|
18
|
+
the matching `lang` for Hebrew, Persian, Urdu). The `lang` attribute
|
|
19
|
+
drives font-stack selection, hyphenation, locale-aware speech
|
|
20
|
+
synthesis, and search-engine indexing — `dir` alone isn't enough.
|
|
21
|
+
Three patterns cover the common cases:
|
|
22
|
+
|
|
23
|
+
- **Full-page RTL.** `<html dir="rtl" lang="ar">`. Everything inside inherits.
|
|
24
|
+
- **Mixed-language subtree.** Nest `<section dir="ltr" lang="en">…</section>` (or vice versa) when an embedded block uses a different script. Code samples, English citations, foreign brand names.
|
|
25
|
+
- **User-generated content of unknown direction.** `dir="auto"` on the paragraph. The browser resolves direction from the first strong directional character in the run.
|
|
26
|
+
|
|
27
|
+
Setting `lang` without `dir` is fine **at the document root in a
|
|
28
|
+
default-LTR page** — English doesn't need `dir="ltr"` there because
|
|
29
|
+
the bidi base direction is already LTR. Inside any opposite-direction
|
|
30
|
+
ancestor, `lang` does not reset the inherited base direction, so set
|
|
31
|
+
both `lang` and `dir` on the subtree (`<section dir="ltr" lang="en">`).
|
|
32
|
+
Setting `dir` without `lang` is rarely correct — at minimum drop the
|
|
33
|
+
appropriate ISO-639 tag in.
|
|
34
|
+
|
|
35
|
+
## Logical properties first
|
|
36
|
+
|
|
37
|
+
Hardcoded `left` / `right` is a bug for any layout that might render
|
|
38
|
+
RTL. Use logical properties on the inline axis. Use them on the block
|
|
39
|
+
axis when the writing-mode varies; physical otherwise.
|
|
40
|
+
|
|
41
|
+
| Logical | LTR resolves to | RTL resolves to |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `margin-inline-start` / `padding-inline-start` / `inset-inline-start` | left | right |
|
|
44
|
+
| `margin-inline-end` / `padding-inline-end` / `inset-inline-end` | right | left |
|
|
45
|
+
| `border-inline-start` | border-left | border-right |
|
|
46
|
+
| `border-start-start-radius` | border-top-left-radius | border-top-right-radius |
|
|
47
|
+
| `text-align: start` / `text-align: end` | left / right | right / left |
|
|
48
|
+
| `inline-size` / `block-size` | width / height | width / height |
|
|
49
|
+
|
|
50
|
+
Browser support: core inline-axis logical properties are Baseline
|
|
51
|
+
Widely Available (Chrome 87, Safari 14.1, Firefox 66; ≥95% global as
|
|
52
|
+
of 2026-05).
|
|
53
|
+
|
|
54
|
+
**Tailwind v4 changes the answer for new projects.** v4.0 (2025-01-22)
|
|
55
|
+
folded inline-axis logical utilities into core (`ms-*`, `me-*`, `ps-*`,
|
|
56
|
+
`pe-*`, `start-*`, `end-*`). v4.2 (2026-02-18) added the block-axis
|
|
57
|
+
set (`mbs-*`, `mbe-*`, `pbs-*`, `pbe-*`) and renamed the inset
|
|
58
|
+
utilities: `start-*` / `end-*` are deprecated (still work) in favor
|
|
59
|
+
of `inset-s-*` / `inset-e-*`. The `tailwindcss-rtl` plugin is obsolete.
|
|
60
|
+
Don't write `[dir="rtl"]:` overrides for spacing on Tailwind v4.
|
|
61
|
+
|
|
62
|
+
## Bidirectional text
|
|
63
|
+
|
|
64
|
+
UAX #9 rev 51 (Sept 2025) is a version stamp for Unicode 17.0. No
|
|
65
|
+
algorithm change; `max_depth = 125` is permanently locked forward.
|
|
66
|
+
|
|
67
|
+
UAX #9 defines two distinct families of bidi formatting characters
|
|
68
|
+
that solve different problems:
|
|
69
|
+
|
|
70
|
+
- **Isolate controls** (modern, prefer these): U+2066 LRI, U+2067 RLI, U+2068 FSI — opened with these, all closed with U+2069 PDI. An isolated run does not affect, and is not affected by, the surrounding paragraph's bidi resolution. Use FSI when the embedded run's direction is unknown ahead of time.
|
|
71
|
+
- **Embedding / override controls** (legacy): U+202A LRE, U+202B RLE, U+202D LRO, U+202E RLO — all closed with U+202C PDF. These nest within the surrounding paragraph rather than isolating from it; LRO/RLO additionally force a direction onto neutral characters. Newer code should use isolates; touch embeddings only when interoperating with text from systems that emit them.
|
|
72
|
+
|
|
73
|
+
**Use `<bdi>` in HTML; in plain text, pick the isolate that matches
|
|
74
|
+
what you know about the run.** UAX #9 §2.7: *"where available, markup
|
|
75
|
+
should be used instead of the explicit formatting characters."*
|
|
76
|
+
`<bdi>` has been Baseline Widely Available since January 2020.
|
|
77
|
+
Reach for control characters only in plain-text contexts (logs,
|
|
78
|
+
plain-text emails, terminal output). When you do:
|
|
79
|
+
|
|
80
|
+
- **LRI U+2066 + PDI U+2069** for known-LTR runs (English name in an Arabic paragraph, code-style identifiers, phone numbers).
|
|
81
|
+
- **RLI U+2067 + PDI U+2069** for known-RTL runs (Arabic name in an English paragraph).
|
|
82
|
+
- **FSI U+2068 + PDI U+2069** for unknown direction (UGC where the author and language can vary).
|
|
83
|
+
|
|
84
|
+
Don't reach for FSI as the default — it auto-detects from the first
|
|
85
|
+
strong character, which is the wrong choice when you already know
|
|
86
|
+
what direction the run should be.
|
|
87
|
+
|
|
88
|
+
`dir="auto"` on a paragraph or `<bdi>` lets the browser detect
|
|
89
|
+
direction from the first strong directional character. Best for
|
|
90
|
+
user-generated content where direction isn't known at author time.
|
|
91
|
+
|
|
92
|
+
## What mirrors and what doesn't
|
|
93
|
+
|
|
94
|
+
Mirroring isn't universal. The rules below are unanimous across
|
|
95
|
+
Material 3 RTL guidance and Apple HIG internationalization.
|
|
96
|
+
|
|
97
|
+
**Must mirror:**
|
|
98
|
+
|
|
99
|
+
- Directional arrows (back / forward / next / previous), navigation rail position, tab order, calendar-grid weekday order.
|
|
100
|
+
- Slider fill direction and **non-media** progress-bar fill (a download progress bar, a form-completion bar, an upload status). Media scrubbers stay LTR — see the Media row below.
|
|
101
|
+
- Checkbox-and-label position. Label sits to the right in LTR, to the left in RTL.
|
|
102
|
+
- Phone-number and IBAN affordances when the surrounding paragraph is RTL but the value itself is LTR — wrap the value in `<bdi dir="ltr">` (or `<span dir="ltr">`) so the digits don't reflow. Bare `<bdi>` is not enough: phone numbers and account numbers contain mostly weak / neutral characters, so first-strong direction detection is unreliable. Force LTR explicitly.
|
|
103
|
+
|
|
104
|
+
**Must not mirror:**
|
|
105
|
+
|
|
106
|
+
- Clock faces. Clockwise is universal.
|
|
107
|
+
- Circular refresh / sync / reload icons. Same reason.
|
|
108
|
+
- Media playback controls (play / pause / fast-forward / rewind) **and the media scrubber / progress timeline**. They represent tape direction, not reading direction.
|
|
109
|
+
- Charts and graphs. X-axis stays mathematical, not linguistic.
|
|
110
|
+
- Photographs, brand logos, physical-object icons (camera, keyboard, headphones). Identity over direction.
|
|
111
|
+
|
|
112
|
+
**Numerals are not a mirroring decision.** They follow locale, not
|
|
113
|
+
paragraph direction. Arabic-Indic digits carry bidi class **AN**, not
|
|
114
|
+
EN — affects how they sit inside mixed-direction lines but does not
|
|
115
|
+
flip them.
|
|
116
|
+
|
|
117
|
+
**Single live conflict between platforms:** the search icon. SF Symbols
|
|
118
|
+
ships an RTL `magnifyingglass` variant (Apple flips it). Material 3
|
|
119
|
+
says don't flip the magnifying glass (handle stays bottom-right).
|
|
120
|
+
Decide per-platform; don't synthesize a single rule.
|
|
121
|
+
|
|
122
|
+
## Typography rules anchored here
|
|
123
|
+
|
|
124
|
+
Two RTL-coupled typography rules sit in this file because they cause
|
|
125
|
+
breakage at the layout level. The full Arabic / Hebrew typography
|
|
126
|
+
guide (font picks, harakat line-height, OpenType shaping, mixed-script
|
|
127
|
+
fallback chains) belongs in a future `craft/arabic-hebrew-typography.md`.
|
|
128
|
+
|
|
129
|
+
- **Never apply CSS `letter-spacing` to Arabic runs.** alreq treats
|
|
130
|
+
letter-spacing as a boundary concept, not a uniform tracking value.
|
|
131
|
+
Applying tracking breaks the cursive joining the script depends on.
|
|
132
|
+
- **Body type for Arabic runs ~14-18 px with line-height 1.5-1.75** to
|
|
133
|
+
give harakat (diacritics) clearance. Latin defaults are too tight.
|
|
134
|
+
|
|
135
|
+
## Native mobile RTL parity
|
|
136
|
+
|
|
137
|
+
Web RTL handling does not auto-translate to mobile. Each platform has
|
|
138
|
+
its own direction primitive. Skills that emit web-only artifacts can
|
|
139
|
+
skim this section; it's the entry point for skills that ship to
|
|
140
|
+
mobile (mobile-onboarding, mobile-app, etc.).
|
|
141
|
+
|
|
142
|
+
| Platform | Direction primitive | Spacing |
|
|
143
|
+
|---|---|---|
|
|
144
|
+
| iOS UIKit | `semanticContentAttribute = .forceRightToLeft` | `NSDirectionalEdgeInsets` |
|
|
145
|
+
| iOS SwiftUI | `.environment(\.layoutDirection, .rightToLeft)` | `EdgeInsets` with `leading` / `trailing` |
|
|
146
|
+
| Android Compose | `CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl)` | `PaddingValues` accepts start / end |
|
|
147
|
+
| Flutter | `Directionality(textDirection: TextDirection.rtl)` | `EdgeInsetsDirectional.fromSTEB(...)` |
|
|
148
|
+
| React Native | `I18nManager.forceRTL(true)` (requires native reload; no `forceLTR` parity, no `react-native-web` support) | `marginStart` / `marginEnd` |
|
|
149
|
+
|
|
150
|
+
The rule across all platforms: prefer the directional primitive over
|
|
151
|
+
the absolute one. `EdgeInsets.left/right` in Flutter, `paddingLeft` /
|
|
152
|
+
`paddingRight` in Android, leading-vs-trailing in iOS — these are bugs
|
|
153
|
+
waiting for an Arabic deployment.
|
|
154
|
+
|
|
155
|
+
## Forms in RTL
|
|
156
|
+
|
|
157
|
+
Form fields commonly mix scripts. Three rules cover most of it.
|
|
158
|
+
|
|
159
|
+
- **`<input dir="auto">`** for any field whose value's direction is uncertain (search boxes, comment fields, free-text inputs). The browser detects from the first strong directional character.
|
|
160
|
+
- **Force LTR on intrinsically-LTR fields** even inside an RTL paragraph: email, URL, phone, IBAN, credit-card. `<input type="email" dir="ltr">`.
|
|
161
|
+
- **Wrap rendered values in `<bdi>`** when displaying mixed-script content (a username inside a paragraph, a model number inside a description). Stops the surrounding direction from rearranging the embedded value. For values whose direction is fixed and weak-character-heavy (phone, IBAN, card number), use `<bdi dir="ltr">` rather than bare `<bdi>` so first-strong detection doesn't misclassify.
|
|
162
|
+
|
|
163
|
+
## Common mistakes (lint these)
|
|
164
|
+
|
|
165
|
+
Mechanically lintable items can be flagged from CSS / source alone.
|
|
166
|
+
Script-aware items need to detect Arabic / Hebrew runs in the
|
|
167
|
+
rendered text and have legitimate exceptions (chart axes, physical
|
|
168
|
+
icons, platform-specific placement).
|
|
169
|
+
|
|
170
|
+
**Mechanically lintable:**
|
|
171
|
+
|
|
172
|
+
- Hardcoded `left` / `right` / `text-align: left` in new CSS — bug for any layout that may render RTL. Exceptions: chart x-axes, physical-object icons, platform-pinned UI like a status-bar clock. Lint with an allow-list rather than blanket banning.
|
|
173
|
+
- "Tailwind v4.2 logical-utility rename is `inline-s-*` / `inline-e-*`" — wrong family. Those are size utilities. The inset rename is `inset-s-*` / `inset-e-*`.
|
|
174
|
+
- "WebKit doesn't support U+2066-U+2069" — wrong, they're interoperable across modern browsers. The "still missing" claim traces to a stale 2015 W3C test snapshot.
|
|
175
|
+
- Setting `dir="rtl"` without `lang="ar"` (or matching). Lint together; `dir` alone misses the font-stack and locale path.
|
|
176
|
+
- Flutter `EdgeInsets.left/right` in code that needs to render RTL. Use `EdgeInsetsDirectional.start/end`.
|
|
177
|
+
|
|
178
|
+
**Needs script detection (will false-positive without it):**
|
|
179
|
+
|
|
180
|
+
- "Use `text-justify: kashida` for Arabic" — no browser implements it. CSS `text-align: justify` adds inter-word spacing and looks unnatural in Arabic; kashida elongation is the correct form, but it isn't shippable on the web today.
|
|
181
|
+
- Italics on Arabic or Hebrew text. Neither script has an italic tradition.
|
|
182
|
+
- CSS `letter-spacing` applied to Arabic. Breaks cursive joining (alreq treats it as a boundary concept, not a uniform tracking value).
|
|
183
|
+
- Lorem Ipsum used for RTL prototyping. Arabic word lengths, connection behaviors, and vertical extents differ; use real Arabic / Hebrew text.
|
|
184
|
+
|
|
185
|
+
**HTML semantics:**
|
|
186
|
+
|
|
187
|
+
- Reaching for CSS bidi controls (`unicode-bidi: isolate` / `plaintext` / `embed`) for inline runs when `<bdi>` or a `dir`-bearing element does the job. Prefer semantic isolation in HTML for inline content; `unicode-bidi: plaintext` operates on a different surface (it changes how base direction is determined for each plaintext paragraph in a block) and should only be used when that block-level paragraph behavior is explicitly required and tested. The two are not drop-in equivalents — don't lint one as a replacement for the other.
|
|
188
|
+
- Bare `<bdi>` around phone / IBAN / card numbers in an RTL paragraph. First-strong detection on weak/neutral characters is unreliable; force `dir="ltr"` explicitly.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<!-- Vendored from nexu-io/open-design@78d5637cb49d (craft/state-coverage.md), Apache-2.0, Copyright 2026 Open Design contributors. Modified by Pugi: this header line added; the text below is otherwise verbatim. Paths it names (apps/daemon/src/lint-artifact.ts, pnpm lint:craft, od.craft.requires) are upstream's, not Pugi's: the Pugi port of the linter is packages/opencode/src/session/design-lint.ts. See NOTICE.md in the skill root. -->
|
|
2
|
+
|
|
3
|
+
# State coverage craft rules
|
|
4
|
+
|
|
5
|
+
Universal rules for what every interactive surface must render. The active
|
|
6
|
+
`DESIGN.md` decides how each state looks; this file decides which states must
|
|
7
|
+
exist and what they must contain. The single most reliable AI-design failure
|
|
8
|
+
is shipping only the populated state.
|
|
9
|
+
|
|
10
|
+
> Distilled from WCAG 2.2, NN/g, Material Design 3, Apple HIG, and Baymard
|
|
11
|
+
> Institute checkout research.
|
|
12
|
+
|
|
13
|
+
## The five required states
|
|
14
|
+
|
|
15
|
+
Every surface that fetches, transforms, or accepts data must render all five.
|
|
16
|
+
|
|
17
|
+
| State | Triggered when | Must contain |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| **Loading** | Data is in flight | Skeleton, spinner, or shell — plus a 15 s "taking longer than expected" fallback |
|
|
20
|
+
| **Empty** | No records yet, or query returned nothing | Headline, plain explanation, primary CTA |
|
|
21
|
+
| **Error** | Fetch failed, server failure, validation rejection | Plain-language cause, recovery action, preserved user input |
|
|
22
|
+
| **Populated** | Data present, primary case | The state the design was actually drawn for |
|
|
23
|
+
| **Edge** | Extreme volume, long strings, missing optional fields, RTL or long-word content, partial network | Layout that does not break |
|
|
24
|
+
|
|
25
|
+
Render-and-screenshot test: every list, table, card, form, and panel in the
|
|
26
|
+
artifact has all five. Missing states are the most common silent failure of
|
|
27
|
+
AI-generated UI.
|
|
28
|
+
|
|
29
|
+
**Test matrix.** Concrete edge scenarios the surface must survive:
|
|
30
|
+
|
|
31
|
+
| Skill type | Edge scenario |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Dashboard / table | 10,000+ rows, all numeric columns, sort + filter applied |
|
|
34
|
+
| Mobile card / list | 200-char title, missing avatar, missing secondary CTA |
|
|
35
|
+
| Form | All optional fields empty, all required fields at max length |
|
|
36
|
+
| Search results | Single-character query, query with only special chars, 1,000+ result count |
|
|
37
|
+
| Detail view | Missing all optional metadata, RTL primary content with LTR embeds |
|
|
38
|
+
|
|
39
|
+
## Form-specific states
|
|
40
|
+
|
|
41
|
+
Forms add three states on top of the five.
|
|
42
|
+
|
|
43
|
+
| State | Triggered when | Behavior |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| **Untouched** | Field has not yet had focus | Default styling; no validation messages |
|
|
46
|
+
| **Dirty (valid)** | User typed and field passes validation | Persistent helper text remains; no success-coloring |
|
|
47
|
+
| **Submitted-pending** | Submit clicked, awaiting server | Submit button enters loading state; fields lock against re-submission |
|
|
48
|
+
|
|
49
|
+
Validation timing: validate **on blur**, not on first keystroke. For password
|
|
50
|
+
and similar live fields, validate on each keystroke *only after the first
|
|
51
|
+
blur*. Remove the error message the instant input becomes valid.
|
|
52
|
+
|
|
53
|
+
## Empty state composition
|
|
54
|
+
|
|
55
|
+
Empty is not the absence of state. It is its own state with a job.
|
|
56
|
+
|
|
57
|
+
- **First-use empty** — illustration + headline + value sentence + primary CTA. The empty is the onboarding moment.
|
|
58
|
+
- **No-results empty** — echo the query, suggest alternatives, never leave a true blank.
|
|
59
|
+
- **Cleared empty** — celebratory phrasing, optional next-action.
|
|
60
|
+
- **Error-as-empty** — never. An error is its own state with recovery information; do not collapse error into empty.
|
|
61
|
+
|
|
62
|
+
**Server-driven vs client-driven.** When a search or query API can return fallback content in the empty payload (suggestions, related categories, popular results), prefer that over a client-side echo. Algolia, Elastic, and most modern search backends support this — the server has more context for what "no results, but maybe try X" should mean.
|
|
63
|
+
|
|
64
|
+
## Error state composition
|
|
65
|
+
|
|
66
|
+
Every error must answer three questions, in this order:
|
|
67
|
+
|
|
68
|
+
1. **What happened.** "Your card was declined." Not "Something went wrong."
|
|
69
|
+
2. **Why, if knowable.** "Insufficient funds." Or "Network unreachable — check your connection."
|
|
70
|
+
3. **What the user can do.** A retry button, an alternative path, or a support link.
|
|
71
|
+
|
|
72
|
+
Preserve user input across the error. The form must not clear on submit
|
|
73
|
+
failure.
|
|
74
|
+
|
|
75
|
+
Severity tiers:
|
|
76
|
+
|
|
77
|
+
- **Field-level** — red border, inline message, focus moves to the field.
|
|
78
|
+
- **Form-level** — error summary banner at top + per-field markers.
|
|
79
|
+
- **Section-level** — inline panel with retry, surrounding sections still functional.
|
|
80
|
+
- **Page-level** — full error state with illustration and recovery CTA.
|
|
81
|
+
- **App-level** — persistent banner or modal for critical loss-of-functionality.
|
|
82
|
+
|
|
83
|
+
Match severity to surface scope. A field validation failure does not warrant
|
|
84
|
+
a page-level error.
|
|
85
|
+
|
|
86
|
+
**Retry discipline.** A retry surface is not a button alone. It has timing rules:
|
|
87
|
+
|
|
88
|
+
- First retry fires immediately on user click.
|
|
89
|
+
- Second and third retries use exponential backoff: 2 s, 4 s, 8 s max.
|
|
90
|
+
- After 3 failed retries, replace "Retry" with "Contact support" plus a copyable error ID. The user has done their job; the system now needs a human.
|
|
91
|
+
- Show "Last attempted: Xs ago" on the error surface after the first retry, so the user knows how stale the failure is.
|
|
92
|
+
|
|
93
|
+
## Loading state thresholds
|
|
94
|
+
|
|
95
|
+
Pick the indicator by expected duration, not by what's available in the
|
|
96
|
+
component library.
|
|
97
|
+
|
|
98
|
+
| Duration | Indicator |
|
|
99
|
+
|---|---|
|
|
100
|
+
| 0–300 ms | None. Render synchronously; users perceive no delay. |
|
|
101
|
+
| 300 ms – 2 s | Subtle spinner or skeleton. |
|
|
102
|
+
| 2 – 10 s | Skeleton matched to expected layout, or labelled spinner ("Loading payments…"). |
|
|
103
|
+
| 10 – 30 s | Determinate progress bar with cancel option. |
|
|
104
|
+
| 30 – 60 s | Progress bar with explicit cancel affordance. The "taking longer than expected" notice already appeared at 15 s; do not repeat it. |
|
|
105
|
+
| 60 s+ | Stop animation. Show error with retry, cancel, or continue. |
|
|
106
|
+
|
|
107
|
+
Never leave a spinner running indefinitely. Start a timeout on every request.
|
|
108
|
+
|
|
109
|
+
## ARIA and focus rules
|
|
110
|
+
|
|
111
|
+
State changes must be announced and focused correctly.
|
|
112
|
+
|
|
113
|
+
| Change | ARIA | Focus action |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| Inline error on submit | `role="alert"` on the message | Move focus to first error field |
|
|
116
|
+
| Toast / non-urgent confirmation | `role="status"` (polite live region) | Do not move focus |
|
|
117
|
+
| Critical error or destructive confirmation | `role="alertdialog"` (assertive) | Move focus to dialog |
|
|
118
|
+
| Loading begins | `role="status"` announcement ("Loading…") | Do not move focus to spinner |
|
|
119
|
+
| Loading ends, content appears | — | Move focus to loaded content if action was user-initiated |
|
|
120
|
+
|
|
121
|
+
Live region containers must exist in the DOM before content is injected.
|
|
122
|
+
Adding `aria-live` simultaneously with content does not trigger an
|
|
123
|
+
announcement.
|
|
124
|
+
|
|
125
|
+
## Common mistakes (lint these)
|
|
126
|
+
|
|
127
|
+
- Surface renders only the populated state; loading, empty, error, and edge are absent.
|
|
128
|
+
- Empty state is a literal blank or "No data" text with no headline, explanation, or action.
|
|
129
|
+
- Error message reads "Something went wrong" with no cause or recovery.
|
|
130
|
+
- Spinner with no timeout; runs indefinitely on slow or failed requests.
|
|
131
|
+
- Submit clears form fields on validation failure, forcing re-entry.
|
|
132
|
+
- Inline validation fires on first keystroke instead of on blur.
|
|
133
|
+
- Full-page loading replaces the chrome when only one section is fetching.
|
|
134
|
+
- Toast appears at a different screen position than previous toasts in the same artifact.
|
|
135
|
+
- Color alone conveys error state — no icon, no text label.
|
|
136
|
+
- Auto-dismissing toast cannot be paused on hover or focus (WCAG SC 2.2.1).
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
<!-- Vendored from nexu-io/open-design@78d5637cb49d (craft/typography-hierarchy-editorial.md), Apache-2.0, Copyright 2026 Open Design contributors. Modified by Pugi: this header line added; the text below is otherwise verbatim. Paths it names (apps/daemon/src/lint-artifact.ts, pnpm lint:craft, od.craft.requires) are upstream's, not Pugi's: the Pugi port of the linter is packages/opencode/src/session/design-lint.ts. See NOTICE.md in the skill root. -->
|
|
2
|
+
|
|
3
|
+
# Editorial typography hierarchy craft rules
|
|
4
|
+
|
|
5
|
+
Extends `typography.md` + `typography-hierarchy.md`. Defines hierarchy
|
|
6
|
+
behavior for editorial surfaces: long-form articles, magazine layouts,
|
|
7
|
+
digital guides, editorial landing pages, and blog posts.
|
|
8
|
+
|
|
9
|
+
> Opt in via `od.craft.requires: [typography, typography-hierarchy, typography-hierarchy-editorial]`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## What "editorial" means here
|
|
14
|
+
|
|
15
|
+
Editorial hierarchy means the pacing is authored the way a print art director
|
|
16
|
+
paces a spread: entry point, tension, rest, disruption, resolution. The reader
|
|
17
|
+
is moved through content rather than given a uniform reading surface. SaaS
|
|
18
|
+
hierarchy is additive — elements stack and each gets its turn. Editorial
|
|
19
|
+
hierarchy is compositional — elements are weighted against each other and
|
|
20
|
+
some are deliberately suppressed so others can breathe.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Editorial hierarchy principles
|
|
25
|
+
|
|
26
|
+
### 1. Dramatic scale jumps
|
|
27
|
+
|
|
28
|
+
Editorial type scales are not gradual. The gap between display and body
|
|
29
|
+
is large — often 3–5× — because the display element is not just a heading,
|
|
30
|
+
it is a visual event.
|
|
31
|
+
|
|
32
|
+
| Level | Typical range | Notes |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| Display / lede | 56–96 px | (editorial override) May intentionally exceed the default `typography.md` display range |
|
|
35
|
+
| Deck / standfirst | 18–24 px | Large jump down — intentional |
|
|
36
|
+
| Body | 16–18 px | Close to deck is fine; they're in the same reading register |
|
|
37
|
+
| Pull quote | 28–40 px | Disrupts body rhythm; treated as a visual break, not a heading |
|
|
38
|
+
| Caption / label | 11–13 px | Minimal — never competes with body |
|
|
39
|
+
|
|
40
|
+
The gap between display and deck is the editorial signature. A small step
|
|
41
|
+
here reads as SaaS, not editorial.
|
|
42
|
+
|
|
43
|
+
### 2. Whitespace carries hierarchy
|
|
44
|
+
|
|
45
|
+
Editorial hierarchy is not announced by a heavy heading. It is created by
|
|
46
|
+
the space that surrounds an element. An article title in a moderate weight
|
|
47
|
+
surrounded by generous whitespace outranks a bold heading crammed against
|
|
48
|
+
its content.
|
|
49
|
+
|
|
50
|
+
Rules:
|
|
51
|
+
- Above-the-fold display element: minimum 2× the line-height in space above
|
|
52
|
+
and below before body begins.
|
|
53
|
+
- Pull quotes: full column margin on both sides, or break the grid entirely.
|
|
54
|
+
- Section breaks: use space as the default hierarchy signal. Separators (rules, dingbats,
|
|
55
|
+
folios, chapter marks) are allowed only when they reinforce publication identity or
|
|
56
|
+
distinguish unrelated content. For RTL layouts, mirror or adapt separators using
|
|
57
|
+
logical directions (inline-start, inline-end) rather than physical (left, right).
|
|
58
|
+
- Caption clusters: tighter internal spacing, larger gap from the body above.
|
|
59
|
+
|
|
60
|
+
### 3. Restrained bold
|
|
61
|
+
|
|
62
|
+
Editorial systems use weight sparingly. The display element is often set in
|
|
63
|
+
a light or regular weight — hierarchy is carried by scale and space, not mass.
|
|
64
|
+
|
|
65
|
+
Bold in editorial context means: this word/phrase matters beyond the sentence.
|
|
66
|
+
It is not used for section labels, UI chrome, or navigation. One to two bold
|
|
67
|
+
phrases per 400 words of body copy is a working upper bound.
|
|
68
|
+
|
|
69
|
+
If everything important is bold, nothing is.
|
|
70
|
+
|
|
71
|
+
### 4. Display tracking
|
|
72
|
+
|
|
73
|
+
Negative tracking at large sizes is mandatory for Latin display. At editorial display sizes
|
|
74
|
+
(56 px+), tracking should be `-0.02em` to `-0.05em` (editorial override;
|
|
75
|
+
see `typography.md` §letter-spacing for the default range). Light display
|
|
76
|
+
weights may go tighter within this range.
|
|
77
|
+
|
|
78
|
+
**Script-aware exception:** For Arabic, Persian, and Urdu (cursive-joining scripts),
|
|
79
|
+
keep tracking at `0` — negative letter-spacing breaks cursive joining (see `rtl-and-bidi`).
|
|
80
|
+
Hebrew uses logical spacing rules but is not cursive-joining; consult `rtl-and-bidi`
|
|
81
|
+
for right-to-left baseline adjustments. Hierarchy in these scripts is carried by size,
|
|
82
|
+
weight, and whitespace, not tracking.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### 5. Pull quotes as rhythm disruption
|
|
87
|
+
|
|
88
|
+
A pull quote is not a blockquote. It is a visual interrupt.
|
|
89
|
+
|
|
90
|
+
| Property | Behavior |
|
|
91
|
+
|---|---|
|
|
92
|
+
| Scale | 28–40 px — above body, below display |
|
|
93
|
+
| Weight | Regular or light — never bold |
|
|
94
|
+
| Tracking | Slightly negative (`-0.01em`) or zero |
|
|
95
|
+
| Alignment | Break from body column — full width, or offset inline-start/inline-end |
|
|
96
|
+
| Spacing | Large above and below — equal to or greater than a section break |
|
|
97
|
+
| Color | May use `var(--accent)` as the only accent use on the page |
|
|
98
|
+
|
|
99
|
+
Pull quotes placed at regular intervals destroy their effect. One per
|
|
100
|
+
long-form article is usually correct. Two is a maximum.
|
|
101
|
+
|
|
102
|
+
### 6. Body measure and reading rhythm
|
|
103
|
+
|
|
104
|
+
Long-form body copy: 60–70 ch line length. Tighter than the `typography.md`
|
|
105
|
+
max because editorial reading is sustained, not scanning.
|
|
106
|
+
|
|
107
|
+
Line height: `1.6`–`1.7` for serif body. Slightly more generous than the
|
|
108
|
+
universal rule because editorial bodies are set at a reading size, not a UI
|
|
109
|
+
size.
|
|
110
|
+
|
|
111
|
+
Do not justify. Use `text-align: start` with a ragged inline-end edge — the
|
|
112
|
+
natural setting for editorial body copy on screen.
|
|
113
|
+
|
|
114
|
+
### 7. Asymmetrical rhythm
|
|
115
|
+
|
|
116
|
+
Uniform section spacing reads as a template. Editorial pacing alternates
|
|
117
|
+
between compression and expansion:
|
|
118
|
+
|
|
119
|
+
- Dense section → spacious section → medium section.
|
|
120
|
+
- A tightly spaced image caption cluster immediately after a generous
|
|
121
|
+
body paragraph creates productive tension.
|
|
122
|
+
- The final section before a pull quote should tighten; after it, release.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Editorial hierarchy table
|
|
127
|
+
|
|
128
|
+
| Element | Scale | Weight | Tracking | Leading | Spacing role |
|
|
129
|
+
|---|---|---|---|---|---|
|
|
130
|
+
| Display headline | 56–96 px | Light or regular | `-0.02em` to `-0.05em` | `1.0`–`1.1` | Event — generous above/below |
|
|
131
|
+
| Deck / standfirst | 18–24 px | Regular | `0` | `1.4`–`1.5` | Transitional — moderate gap below |
|
|
132
|
+
| Byline / dateline | 12–14 px | Regular or medium | `0.02em`–`0.04em` | `1.5` | Recedes — tight gap below |
|
|
133
|
+
| Body | 16–18 px | Regular | `0` | `1.6`–`1.7` | Baseline — rhythm carrier |
|
|
134
|
+
| Pull quote | 28–40 px | Regular or light | `-0.01em` (Latin only; 0 for joining scripts) | `1.2`–`1.3` | Interrupt — large above/below |
|
|
135
|
+
| Image caption | 12–13 px | Regular | `0.01em` | `1.5` | Recedes — tight cluster |
|
|
136
|
+
| Section label | 11–12 px | Medium | `0.06em`–`0.1em` (if caps) | `1.5` | Wayfinding only |
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Anti-patterns
|
|
141
|
+
|
|
142
|
+
- **Bold display headline** — editorial display is usually light or regular.
|
|
143
|
+
Bold display reads as billboard advertising. If the design system's display
|
|
144
|
+
weight is heavy, either use the regular cut or revisit the choice.
|
|
145
|
+
- **Uniform section padding** — every section has the same gap. No pacing.
|
|
146
|
+
The page reads as a list of content blocks.
|
|
147
|
+
- **Pull quote styled as a callout box** — border-left, background tint,
|
|
148
|
+
or card treatment. A pull quote is typographic. It does not need a container.
|
|
149
|
+
- **Deck set at body size** — the gap between headline and deck must be large
|
|
150
|
+
enough to read as a scale event. 18 px minimum for a deck below a 56 px+
|
|
151
|
+
headline.
|
|
152
|
+
- **Heading for every section** — editorial long-form does not require
|
|
153
|
+
a heading at every content shift. Space and pacing are allowed to do the
|
|
154
|
+
work. (guidance)
|
|
155
|
+
- **Positive tracking on display** — wide-tracked display headlines read as
|
|
156
|
+
a branding exercise. Tighten them.
|
|
157
|
+
- **UI chrome in the reading column** — buttons, tags, chip badges interrupting
|
|
158
|
+
prose flow inside the body text column. Functional controls (inline code-copy,
|
|
159
|
+
API anchors, callout toggles) may live inside code and API blocks, but decorative
|
|
160
|
+
badges and UI chrome should live outside the reading measure to keep prose focus clear.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Lint
|
|
165
|
+
|
|
166
|
+
- [ ] Display headline is light or regular weight unless the design system
|
|
167
|
+
specifies otherwise.
|
|
168
|
+
- [ ] Display tracking is within `-0.02em` to `-0.05em` at 56 px+ (Latin only;
|
|
169
|
+
0 for Arabic/Persian/Urdu joining scripts; see `rtl-and-bidi` for Hebrew). (guidance)
|
|
170
|
+
- [ ] Pull-quote tracking: `-0.01em` (Latin only; 0 for joining scripts). (guidance)
|
|
171
|
+
- [ ] Gap between display and deck: display/deck ratio ≥ 1.5× or absolute px delta
|
|
172
|
+
≥ 24 px to read as a scale event. (guidance)
|
|
173
|
+
- [ ] Body line height is `1.6`–`1.7`.
|
|
174
|
+
- [ ] Body measure is 60–70 ch.
|
|
175
|
+
- [ ] Pull quote, if present, breaks the body column (full width or offset).
|
|
176
|
+
- [ ] No more than 2 pull quotes in a single article surface. (long-form only)
|
|
177
|
+
- [ ] Section spacing alternates — one gap ≥ 1.5× baseline rhythm while another
|
|
178
|
+
is ≤ 1.2×. (long-form only — guidance)
|
|
179
|
+
- [ ] Bold used ≤ 2 times per 400 words in body copy. (long-form only)
|
|
180
|
+
- [ ] `var(--accent)` used ≤ 2 times on the full editorial surface (see `color.md` §accent discipline).
|
|
181
|
+
- [ ] Section separators (rules, dingbats) are used only when they
|
|
182
|
+
reinforce publication identity or clearly mark unrelated-content boundaries;
|
|
183
|
+
they must not be used as a hierarchy fallback. (guidance)
|
|
184
|
+
- [ ] Pull quote has no background, border, or container treatment.
|