mirror of
https://github.com/nexu-io/open-design.git
synced 2026-07-03 12:27:55 +08:00
craft: add rtl-and-bidi so OD artifacts don't break for Arabic / Hebrew / Persian users (#595)
* feat(craft): add rtl-and-bidi + opt-ins on blog-post, docs-page, finance-report Module 4 of 5 in the behavioral craft series proposed in #501. Modules 1 (state-coverage, #502) and 2 (animation-discipline, #515) merged. Module 3 (accessibility-baseline, #587) open at time of authoring. Differentiating niche per the corpus prior-art survey: zero existing OSS RTL skill is Apache-2.0, framework-agnostic, and aligned with UAX #9 rev 51. The closest comparators (idanlevi1/rtlify 5★, MIT; skills-il/localization 7★, MIT) are LTR-web-skewed and don't cover Flutter Directionality, RN I18nManager, Compose LocalLayoutDirection, or iOS UIKit semanticContentAttribute / SwiftUI layoutDirection. Three-loop adversarial review pass via Claude Opus 4.7 xhigh effort (codex unavailable). Loop 1 caught five revisions (typography spin-out, WebKit prose compression, mistakes-list trim 12→9, alreq letter-spacing rename dropped, WebKit r94775 specific revision dropped). Loop 2 caught one blocking SwiftUI 4 claim and three nits. Loop 3 said ship. Skill opt-ins picked to avoid PR #587 merge surface: blog-post (long-form text), docs-page (LTR code islands in RTL prose), finance-report (numerals + IBAN + currency). Refs #501. * fix(craft): rtl-and-bidi review fixes (lefarcen 6 findings) - P2 #1 WebKit #50949: bug is RESOLVED FIXED, not still open. Verified directly against bugs.webkit.org. Removed the broken-WebKit framing; the recommendation to prefer <bdi> over CSS now stands on UAX #9 §2.7 ("prefer markup over CSS or control characters") rather than a WebKit bug. Source list updated to drop the dead reference. - P2 #2 isolate vs embedding controls: U+202C PDF is the embedding/override terminator, not an isolate terminator. Split into two families: isolate controls (U+2066/2067/2068 + U+2069 PDI) for modern code, embedding/override controls (U+202A/202B/202D/202E + U+202C PDF) as legacy. Recommend isolates first. - P2 #3 base direction and language: new section covering <html dir lang>, mixed-language subtrees, dir=auto for UGC. Without this, agents can follow every other rule and still ship an LTR document containing Arabic. - P2 #4 phone/IBAN/card values: bare <bdi> is unreliable for weak/neutral character runs; updated must-mirror bullet and forms section to require <bdi dir="ltr">. Added common-mistake entry. - P3 #1 native mobile budget: added a one-line opt-out hint at the top of the section so HTML-only skills know they can skim it. Full split into web/native files deferred — the table is 16 lines on a 176-line file, the cost is bounded. - P3 #2 lintability: restructured "common mistakes" into three groups — mechanically lintable, needs script detection, HTML semantics — with explicit exception language (chart axes, physical-object icons, platform-pinned UI). Avoids false positives in future linting. Reviewed via Claude CLI Opus 4.7 xhigh effort (3 loops on the original draft); these fixes are explicit reviewer responses with WebKit Bugzilla state verified live. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(craft): rtl-and-bidi mrcfps round-2 precision (lang+dir, isolate picks) Two non-blocking precision items: - lang-without-dir scope: previous wording implied English never needs dir="ltr". True only at the document root in a default-LTR page. lang does not reset an inherited bidi base direction, so an <section lang="en"> inside an RTL ancestor still resolves RTL. Reworded to "lang without dir is fine at the document root in a default-LTR page; inside any opposite-direction ancestor, set both." - Plain-text isolate picks: previous wording recommended U+2068 / U+2069 generically. U+2068 is FSI (first-strong auto-detect) — wrong default for known-direction runs, especially weak/neutral-heavy values like phone, IBAN, card numbers (the same class this file forces to LTR in HTML). Split: LRI/PDI for known-LTR, RLI/PDI for known-RTL, FSI/PDI reserved for unknown direction. Added an explicit "don't default to FSI" callout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(craft+skills): rtl-and-bidi mrcfps round-3 — skill-body conflicts + bidi semantic correction P1 BLOCKING — skill-body physical-direction conflicts (mrcfps): - skills/docs-page: "left nav" / "right-rail TOC" / "left-edge accent stripe" survive in skill body even with the rtl-and-bidi opt-in, because craft is injected ABOVE the skill body. An Arabic docs request would still see "Left nav" and emit physical-direction layout. Updated description, lay-out section, and self-check to inline-start / inline-end vocabulary; added a self-check bullet requiring logical CSS on rails and accent. - skills/blog-post: pull-quote "accent rule on the left" updated to "accent rule on the inline-start edge" with a matching note about flipping under dir="rtl". P1 craft semantic correction (mrcfps): - HTML-semantics lint: previous wording equated <bdi dir="auto"> with unicode-bidi: plaintext. Not equivalent. <bdi> isolates an inline run from surrounding bidi resolution; unicode-bidi: plaintext changes how base direction is *determined* for each plaintext paragraph in a block. Different surfaces. Reworded the lint guidance to "prefer semantic isolation in HTML for inline runs; reach for unicode-bidi: plaintext only when that block-level paragraph behavior is explicitly required and tested" — and explicitly flagged that they are not drop-in equivalents to avoid future linters flagging valid CSS with a non-equivalent fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(craft): rtl-and-bidi mrcfps round-4 — split progress-bar from media scrubber Non-blocking precision: prior must-mirror bullet lumped "progress-bar fill" together with sliders, which would have flipped a video / audio scrubber under dir="rtl" — directly conflicting with the must-not-mirror rule for media playback controls (play/pause/FF/rewind represent tape direction, not reading direction). The two cases collide on every audio or video player. - Must-mirror progress bars now scoped to "non-media" (download, upload, form-completion). - Media scrubber / progress timeline added explicitly to the must-not- mirror media bullet. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -67,6 +67,7 @@ A purely behavioral craft file (state-coverage, animation-discipline) is guidanc
|
||||
| `state-coverage.md` | `state-coverage` | Any skill with stateful UI (dashboards, mobile apps, forms, list/table views) |
|
||||
| `animation-discipline.md` | `animation-discipline` | Any skill that ships motion: mobile apps, multi-screen flows, gamified UI, transitions, microinteractions |
|
||||
| `accessibility-baseline.md` | `accessibility-baseline` | Any skill that ships interactive UI: dashboards, forms, mobile flows, anything with focus/labels/keyboard paths |
|
||||
| `rtl-and-bidi.md` | `rtl-and-bidi` | Any skill that ships localized text or layout: blogs, docs, financial tables, mobile apps, anything that may render Arabic / Hebrew / Persian |
|
||||
|
||||
**Partial-stateful skills.** A skill that's mostly static but contains an embedded form, data table, or query surface should opt in. State-coverage rules apply to the stateful component, not the whole page.
|
||||
|
||||
|
||||
186
craft/rtl-and-bidi.md
Normal file
186
craft/rtl-and-bidi.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# RTL and bidirectional craft rules
|
||||
|
||||
Universal rules for right-to-left layout and bidirectional text. The
|
||||
active `DESIGN.md` decides brand visual language; this file decides
|
||||
how that language behaves when the script reads from the right or
|
||||
mixes direction within a line.
|
||||
|
||||
> Grounded in primary sources: Unicode UAX #9 revision 51 (Sept 2025)
|
||||
> + Unicode 17.0, CSS Logical Properties Level 1, HTML Living Standard
|
||||
> (`dir`, `<bdi>`), Tailwind v4.0/v4.2 changelogs, W3C alreq,
|
||||
> Material 3 RTL guidance, Apple HIG internationalization.
|
||||
|
||||
## Base direction and language
|
||||
|
||||
Every full-page RTL artifact needs `<html dir="rtl" lang="ar">` (or
|
||||
the matching `lang` for Hebrew, Persian, Urdu). The `lang` attribute
|
||||
drives font-stack selection, hyphenation, locale-aware speech
|
||||
synthesis, and search-engine indexing — `dir` alone isn't enough.
|
||||
Three patterns cover the common cases:
|
||||
|
||||
- **Full-page RTL.** `<html dir="rtl" lang="ar">`. Everything inside inherits.
|
||||
- **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.
|
||||
- **User-generated content of unknown direction.** `dir="auto"` on the paragraph. The browser resolves direction from the first strong directional character in the run.
|
||||
|
||||
Setting `lang` without `dir` is fine **at the document root in a
|
||||
default-LTR page** — English doesn't need `dir="ltr"` there because
|
||||
the bidi base direction is already LTR. Inside any opposite-direction
|
||||
ancestor, `lang` does not reset the inherited base direction, so set
|
||||
both `lang` and `dir` on the subtree (`<section dir="ltr" lang="en">`).
|
||||
Setting `dir` without `lang` is rarely correct — at minimum drop the
|
||||
appropriate ISO-639 tag in.
|
||||
|
||||
## Logical properties first
|
||||
|
||||
Hardcoded `left` / `right` is a bug for any layout that might render
|
||||
RTL. Use logical properties on the inline axis. Use them on the block
|
||||
axis when the writing-mode varies; physical otherwise.
|
||||
|
||||
| Logical | LTR resolves to | RTL resolves to |
|
||||
|---|---|---|
|
||||
| `margin-inline-start` / `padding-inline-start` / `inset-inline-start` | left | right |
|
||||
| `margin-inline-end` / `padding-inline-end` / `inset-inline-end` | right | left |
|
||||
| `border-inline-start` | border-left | border-right |
|
||||
| `border-start-start-radius` | border-top-left-radius | border-top-right-radius |
|
||||
| `text-align: start` / `text-align: end` | left / right | right / left |
|
||||
| `inline-size` / `block-size` | width / height | width / height |
|
||||
|
||||
Browser support: core inline-axis logical properties are Baseline
|
||||
Widely Available (Chrome 87, Safari 14.1, Firefox 66; ≥95% global as
|
||||
of 2026-05).
|
||||
|
||||
**Tailwind v4 changes the answer for new projects.** v4.0 (2025-01-22)
|
||||
folded inline-axis logical utilities into core (`ms-*`, `me-*`, `ps-*`,
|
||||
`pe-*`, `start-*`, `end-*`). v4.2 (2026-02-18) added the block-axis
|
||||
set (`mbs-*`, `mbe-*`, `pbs-*`, `pbe-*`) and renamed the inset
|
||||
utilities: `start-*` / `end-*` are deprecated (still work) in favor
|
||||
of `inset-s-*` / `inset-e-*`. The `tailwindcss-rtl` plugin is obsolete.
|
||||
Don't write `[dir="rtl"]:` overrides for spacing on Tailwind v4.
|
||||
|
||||
## Bidirectional text
|
||||
|
||||
UAX #9 rev 51 (Sept 2025) is a version stamp for Unicode 17.0. No
|
||||
algorithm change; `max_depth = 125` is permanently locked forward.
|
||||
|
||||
UAX #9 defines two distinct families of bidi formatting characters
|
||||
that solve different problems:
|
||||
|
||||
- **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.
|
||||
- **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.
|
||||
|
||||
**Use `<bdi>` in HTML; in plain text, pick the isolate that matches
|
||||
what you know about the run.** UAX #9 §2.7: *"where available, markup
|
||||
should be used instead of the explicit formatting characters."*
|
||||
`<bdi>` has been Baseline Widely Available since January 2020.
|
||||
Reach for control characters only in plain-text contexts (logs,
|
||||
plain-text emails, terminal output). When you do:
|
||||
|
||||
- **LRI U+2066 + PDI U+2069** for known-LTR runs (English name in an Arabic paragraph, code-style identifiers, phone numbers).
|
||||
- **RLI U+2067 + PDI U+2069** for known-RTL runs (Arabic name in an English paragraph).
|
||||
- **FSI U+2068 + PDI U+2069** for unknown direction (UGC where the author and language can vary).
|
||||
|
||||
Don't reach for FSI as the default — it auto-detects from the first
|
||||
strong character, which is the wrong choice when you already know
|
||||
what direction the run should be.
|
||||
|
||||
`dir="auto"` on a paragraph or `<bdi>` lets the browser detect
|
||||
direction from the first strong directional character. Best for
|
||||
user-generated content where direction isn't known at author time.
|
||||
|
||||
## What mirrors and what doesn't
|
||||
|
||||
Mirroring isn't universal. The rules below are unanimous across
|
||||
Material 3 RTL guidance and Apple HIG internationalization.
|
||||
|
||||
**Must mirror:**
|
||||
|
||||
- Directional arrows (back / forward / next / previous), navigation rail position, tab order, calendar-grid weekday order.
|
||||
- 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.
|
||||
- Checkbox-and-label position. Label sits to the right in LTR, to the left in RTL.
|
||||
- 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.
|
||||
|
||||
**Must not mirror:**
|
||||
|
||||
- Clock faces. Clockwise is universal.
|
||||
- Circular refresh / sync / reload icons. Same reason.
|
||||
- Media playback controls (play / pause / fast-forward / rewind) **and the media scrubber / progress timeline**. They represent tape direction, not reading direction.
|
||||
- Charts and graphs. X-axis stays mathematical, not linguistic.
|
||||
- Photographs, brand logos, physical-object icons (camera, keyboard, headphones). Identity over direction.
|
||||
|
||||
**Numerals are not a mirroring decision.** They follow locale, not
|
||||
paragraph direction. Arabic-Indic digits carry bidi class **AN**, not
|
||||
EN — affects how they sit inside mixed-direction lines but does not
|
||||
flip them.
|
||||
|
||||
**Single live conflict between platforms:** the search icon. SF Symbols
|
||||
ships an RTL `magnifyingglass` variant (Apple flips it). Material 3
|
||||
says don't flip the magnifying glass (handle stays bottom-right).
|
||||
Decide per-platform; don't synthesize a single rule.
|
||||
|
||||
## Typography rules anchored here
|
||||
|
||||
Two RTL-coupled typography rules sit in this file because they cause
|
||||
breakage at the layout level. The full Arabic / Hebrew typography
|
||||
guide (font picks, harakat line-height, OpenType shaping, mixed-script
|
||||
fallback chains) belongs in a future `craft/arabic-hebrew-typography.md`.
|
||||
|
||||
- **Never apply CSS `letter-spacing` to Arabic runs.** alreq treats
|
||||
letter-spacing as a boundary concept, not a uniform tracking value.
|
||||
Applying tracking breaks the cursive joining the script depends on.
|
||||
- **Body type for Arabic runs ~14-18 px with line-height 1.5-1.75** to
|
||||
give harakat (diacritics) clearance. Latin defaults are too tight.
|
||||
|
||||
## Native mobile RTL parity
|
||||
|
||||
Web RTL handling does not auto-translate to mobile. Each platform has
|
||||
its own direction primitive. Skills that emit web-only artifacts can
|
||||
skim this section; it's the entry point for skills that ship to
|
||||
mobile (mobile-onboarding, mobile-app, etc.).
|
||||
|
||||
| Platform | Direction primitive | Spacing |
|
||||
|---|---|---|
|
||||
| iOS UIKit | `semanticContentAttribute = .forceRightToLeft` | `NSDirectionalEdgeInsets` |
|
||||
| iOS SwiftUI | `.environment(\.layoutDirection, .rightToLeft)` | `EdgeInsets` with `leading` / `trailing` |
|
||||
| Android Compose | `CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Rtl)` | `PaddingValues` accepts start / end |
|
||||
| Flutter | `Directionality(textDirection: TextDirection.rtl)` | `EdgeInsetsDirectional.fromSTEB(...)` |
|
||||
| React Native | `I18nManager.forceRTL(true)` (requires native reload; no `forceLTR` parity, no `react-native-web` support) | `marginStart` / `marginEnd` |
|
||||
|
||||
The rule across all platforms: prefer the directional primitive over
|
||||
the absolute one. `EdgeInsets.left/right` in Flutter, `paddingLeft` /
|
||||
`paddingRight` in Android, leading-vs-trailing in iOS — these are bugs
|
||||
waiting for an Arabic deployment.
|
||||
|
||||
## Forms in RTL
|
||||
|
||||
Form fields commonly mix scripts. Three rules cover most of it.
|
||||
|
||||
- **`<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.
|
||||
- **Force LTR on intrinsically-LTR fields** even inside an RTL paragraph: email, URL, phone, IBAN, credit-card. `<input type="email" dir="ltr">`.
|
||||
- **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.
|
||||
|
||||
## Common mistakes (lint these)
|
||||
|
||||
Mechanically lintable items can be flagged from CSS / source alone.
|
||||
Script-aware items need to detect Arabic / Hebrew runs in the
|
||||
rendered text and have legitimate exceptions (chart axes, physical
|
||||
icons, platform-specific placement).
|
||||
|
||||
**Mechanically lintable:**
|
||||
|
||||
- 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.
|
||||
- "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-*`.
|
||||
- "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.
|
||||
- Setting `dir="rtl"` without `lang="ar"` (or matching). Lint together; `dir` alone misses the font-stack and locale path.
|
||||
- Flutter `EdgeInsets.left/right` in code that needs to render RTL. Use `EdgeInsetsDirectional.start/end`.
|
||||
|
||||
**Needs script detection (will false-positive without it):**
|
||||
|
||||
- "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.
|
||||
- Italics on Arabic or Hebrew text. Neither script has an italic tradition.
|
||||
- CSS `letter-spacing` applied to Arabic. Breaks cursive joining (alreq treats it as a boundary concept, not a uniform tracking value).
|
||||
- Lorem Ipsum used for RTL prototyping. Arabic word lengths, connection behaviors, and vertical extents differ; use real Arabic / Hebrew text.
|
||||
|
||||
**HTML semantics:**
|
||||
|
||||
- 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.
|
||||
- Bare `<bdi>` around phone / IBAN / card numbers in an RTL paragraph. First-strong detection on weak/neutral characters is unreliable; force `dir="ltr"` explicitly.
|
||||
@@ -25,6 +25,8 @@ od:
|
||||
design_system:
|
||||
requires: true
|
||||
sections: [color, typography, layout, components]
|
||||
craft:
|
||||
requires: [rtl-and-bidi]
|
||||
---
|
||||
|
||||
# Blog Post Skill
|
||||
@@ -44,7 +46,7 @@ Produce a single long-form article page — editorial layout, no chrome.
|
||||
- **Hero image** — a 16:9 placeholder block using a DS-tinted gradient or
|
||||
solid fill (no external images). Add a 1-line caption underneath.
|
||||
- **Body** — alternating prose paragraphs with at least:
|
||||
- 1 pull quote (large display type, accent rule on the left).
|
||||
- 1 pull quote (large display type, accent rule on the inline-start edge so the layout flips correctly under `dir="rtl"`).
|
||||
- 1 figure (image placeholder + caption).
|
||||
- 1 list (numbered or bulleted).
|
||||
- 1 inline blockquote.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: docs-page
|
||||
description: |
|
||||
A documentation page — left nav, scrollable article body, right-rail
|
||||
table of contents. Use when the brief mentions "docs", "documentation",
|
||||
"guide", "API reference", or "tutorial".
|
||||
A documentation page — inline-start nav, scrollable article body,
|
||||
inline-end table of contents. Use when the brief mentions "docs",
|
||||
"documentation", "guide", "API reference", or "tutorial".
|
||||
triggers:
|
||||
- "docs"
|
||||
- "documentation"
|
||||
@@ -21,6 +21,8 @@ od:
|
||||
design_system:
|
||||
requires: true
|
||||
sections: [color, typography, layout, components]
|
||||
craft:
|
||||
requires: [rtl-and-bidi]
|
||||
---
|
||||
|
||||
# Docs Page Skill
|
||||
@@ -34,14 +36,16 @@ Produce a single, three-column documentation page in one HTML file.
|
||||
2. **Pick a topic** from the brief — the page should look like real docs, not
|
||||
a generic wireframe. Concrete API names, command examples, plausible
|
||||
parameters.
|
||||
3. **Lay out** three regions:
|
||||
- **Left nav** (240–280px, sticky): grouped link list, current page bolded
|
||||
with a left-edge accent stripe. 3–5 groups of 4–8 links.
|
||||
3. **Lay out** three regions, expressed on the inline axis so the
|
||||
layout flips correctly under `dir="rtl"`:
|
||||
- **Inline-start nav** (240–280px, sticky): grouped link list, current
|
||||
page bolded with an `inline-start`-edge accent stripe. 3–5 groups
|
||||
of 4–8 links.
|
||||
- **Article body** (max-width ~720px, centered in the middle column):
|
||||
H1, lede paragraph, H2 sections, code blocks, callout boxes (note /
|
||||
warning), inline links, lists.
|
||||
- **Right TOC** (200–240px, sticky): "On this page" with the H2/H3
|
||||
anchors, current section highlighted as the user scrolls.
|
||||
- **Inline-end TOC** (200–240px, sticky): "On this page" with the
|
||||
H2/H3 anchors, current section highlighted as the user scrolls.
|
||||
4. **Write** a single HTML document:
|
||||
- `<!doctype html>` through `</html>`, all CSS inline.
|
||||
- CSS Grid for the three columns; sticky positioning for the rails.
|
||||
@@ -58,6 +62,9 @@ Produce a single, three-column documentation page in one HTML file.
|
||||
border. Not on body text.
|
||||
- Page is readable at 1280w and collapses gracefully below 900w (TOC drops
|
||||
out, nav becomes a top drawer).
|
||||
- Use logical CSS (`margin-inline-start`, `border-inline-start`,
|
||||
`inset-inline-end`, `text-align: start`) on the rails and accent
|
||||
stripe so the layout flips correctly under `dir="rtl"`.
|
||||
|
||||
## Output contract
|
||||
|
||||
|
||||
@@ -24,6 +24,8 @@ od:
|
||||
design_system:
|
||||
requires: true
|
||||
sections: [color, typography, layout, components]
|
||||
craft:
|
||||
requires: [rtl-and-bidi]
|
||||
example_prompt: "Build me a Q3 financial report for an early-stage SaaS — MRR, burn, gross margin, top accounts."
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user