@awarebydefault/display-case 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +309 -0
- package/display-case.prompt.md +64 -0
- package/docs/ai-agents.md +126 -0
- package/docs/cli.md +99 -0
- package/docs/configuration.md +410 -0
- package/docs/documentation-panel.md +50 -0
- package/docs/examples/README.md +14 -0
- package/docs/examples/multi-variant.case.tsx +30 -0
- package/docs/examples/plain.case.tsx +22 -0
- package/docs/examples/tweak-control.placard.md +80 -0
- package/docs/examples/tweaks.case.tsx +39 -0
- package/docs/hierarchy.md +59 -0
- package/docs/quick-start.md +78 -0
- package/docs/style-engines.md +180 -0
- package/docs/testing.md +245 -0
- package/docs/theming.md +97 -0
- package/docs/tweaks.md +75 -0
- package/docs/writing-cases.md +144 -0
- package/docs/writing-placard-docs.md +194 -0
- package/package.json +113 -0
- package/skills/display-case-author-case/README.md +20 -0
- package/skills/display-case-author-case/SKILL.md +40 -0
- package/skills/display-case-author-placard-doc/README.md +24 -0
- package/skills/display-case-author-placard-doc/SKILL.md +65 -0
- package/skills/display-case-review/README.md +19 -0
- package/skills/display-case-review/SKILL.md +30 -0
- package/skills/display-case-snapshot/README.md +20 -0
- package/skills/display-case-snapshot/SKILL.md +29 -0
- package/src/checks/a11y-scanner.test.ts +240 -0
- package/src/checks/a11y-scanner.ts +410 -0
- package/src/checks/check-text.test.ts +53 -0
- package/src/checks/check-text.ts +78 -0
- package/src/checks/check.test.ts +194 -0
- package/src/checks/check.ts +473 -0
- package/src/checks/providers/pixelmatch-diff.test.ts +79 -0
- package/src/checks/providers/pixelmatch-diff.ts +30 -0
- package/src/checks/providers/playwright-driver.ts +104 -0
- package/src/checks/ssr-check.test.ts +73 -0
- package/src/checks/ssr-check.ts +96 -0
- package/src/checks/structure-check.cross-package.test.ts +165 -0
- package/src/checks/structure-check.test.ts +651 -0
- package/src/checks/structure-check.ts +988 -0
- package/src/checks/tokens-check.test.ts +159 -0
- package/src/checks/tokens-check.ts +162 -0
- package/src/cli.ts +218 -0
- package/src/commands/agents.test.ts +24 -0
- package/src/commands/agents.ts +28 -0
- package/src/commands/init-run.test.ts +123 -0
- package/src/commands/init.test.ts +63 -0
- package/src/commands/init.ts +412 -0
- package/src/commands/publish.test.ts +210 -0
- package/src/commands/publish.ts +292 -0
- package/src/core/affected.test.ts +99 -0
- package/src/core/affected.ts +144 -0
- package/src/core/catalog.test.ts +152 -0
- package/src/core/catalog.ts +92 -0
- package/src/core/discovery.test.ts +184 -0
- package/src/core/discovery.ts +250 -0
- package/src/core/manifest.ts +41 -0
- package/src/core/mdx-lite/__fixtures__/box-stub.tsx +7 -0
- package/src/core/mdx-lite/index.ts +393 -0
- package/src/core/mdx-lite/mdx-lite.test.ts +345 -0
- package/src/core/mdx-plugin.test.ts +60 -0
- package/src/core/mdx-plugin.ts +30 -0
- package/src/flow-step.test-d.ts +39 -0
- package/src/index.test.ts +100 -0
- package/src/index.ts +564 -0
- package/src/render/collect-styles.emotion.test.tsx +114 -0
- package/src/render/collect-styles.test.tsx +72 -0
- package/src/render/collect-styles.ts +33 -0
- package/src/render/documents.test.ts +184 -0
- package/src/render/documents.ts +88 -0
- package/src/render/render-node.test.tsx +160 -0
- package/src/render/render-node.tsx +133 -0
- package/src/render/ssr-primer.test.tsx +25 -0
- package/src/render/ssr-primer.tsx +54 -0
- package/src/render/ssr-render.test.tsx +142 -0
- package/src/render/ssr-render.tsx +63 -0
- package/src/render/ssr-shell.test.tsx +57 -0
- package/src/render/ssr-shell.tsx +54 -0
- package/src/server/prod-server.ts +237 -0
- package/src/server/server.test.ts +117 -0
- package/src/server/server.ts +1039 -0
- package/src/style-engine.test-d.ts +37 -0
- package/src/testing/test-helpers.ts +27 -0
- package/src/types/pixelmatch.d.ts +12 -0
- package/src/ui/browser-entry.tsx +51 -0
- package/src/ui/chrome.css +485 -0
- package/src/ui/design-system/README.md +88 -0
- package/src/ui/design-system/components/controls/Button.case.tsx +52 -0
- package/src/ui/design-system/components/controls/Button.css +89 -0
- package/src/ui/design-system/components/controls/Button.placard.md +14 -0
- package/src/ui/design-system/components/controls/Button.test.tsx +45 -0
- package/src/ui/design-system/components/controls/Button.tsx +41 -0
- package/src/ui/design-system/components/controls/IconButton.case.tsx +52 -0
- package/src/ui/design-system/components/controls/IconButton.css +67 -0
- package/src/ui/design-system/components/controls/IconButton.placard.md +13 -0
- package/src/ui/design-system/components/controls/IconButton.test.tsx +39 -0
- package/src/ui/design-system/components/controls/IconButton.tsx +47 -0
- package/src/ui/design-system/components/controls/Input.case.tsx +50 -0
- package/src/ui/design-system/components/controls/Input.css +52 -0
- package/src/ui/design-system/components/controls/Input.placard.md +12 -0
- package/src/ui/design-system/components/controls/Input.test.tsx +43 -0
- package/src/ui/design-system/components/controls/Input.tsx +45 -0
- package/src/ui/design-system/components/controls/Select.case.tsx +48 -0
- package/src/ui/design-system/components/controls/Select.css +44 -0
- package/src/ui/design-system/components/controls/Select.placard.md +15 -0
- package/src/ui/design-system/components/controls/Select.test.tsx +57 -0
- package/src/ui/design-system/components/controls/Select.tsx +58 -0
- package/src/ui/design-system/components/controls/SelectMenu.case.tsx +100 -0
- package/src/ui/design-system/components/controls/SelectMenu.css +72 -0
- package/src/ui/design-system/components/controls/SelectMenu.placard.md +18 -0
- package/src/ui/design-system/components/controls/SelectMenu.test.tsx +66 -0
- package/src/ui/design-system/components/controls/SelectMenu.tsx +377 -0
- package/src/ui/design-system/components/index.ts +66 -0
- package/src/ui/design-system/components/primer-specimen/ColorRamp.case.tsx +44 -0
- package/src/ui/design-system/components/primer-specimen/ColorRamp.placard.md +15 -0
- package/src/ui/design-system/components/primer-specimen/ColorRamp.tsx +51 -0
- package/src/ui/design-system/components/primer-specimen/DefinitionList.case.tsx +38 -0
- package/src/ui/design-system/components/primer-specimen/DefinitionList.placard.md +15 -0
- package/src/ui/design-system/components/primer-specimen/DefinitionList.tsx +41 -0
- package/src/ui/design-system/components/primer-specimen/FontFamilies.case.tsx +24 -0
- package/src/ui/design-system/components/primer-specimen/FontFamilies.placard.md +12 -0
- package/src/ui/design-system/components/primer-specimen/FontFamilies.tsx +41 -0
- package/src/ui/design-system/components/primer-specimen/GlyphGrid.case.tsx +27 -0
- package/src/ui/design-system/components/primer-specimen/GlyphGrid.placard.md +13 -0
- package/src/ui/design-system/components/primer-specimen/GlyphGrid.tsx +34 -0
- package/src/ui/design-system/components/primer-specimen/LayoutMock.case.tsx +36 -0
- package/src/ui/design-system/components/primer-specimen/LayoutMock.placard.md +7 -0
- package/src/ui/design-system/components/primer-specimen/LayoutMock.tsx +36 -0
- package/src/ui/design-system/components/primer-specimen/SpacingScale.case.tsx +20 -0
- package/src/ui/design-system/components/primer-specimen/SpacingScale.placard.md +12 -0
- package/src/ui/design-system/components/primer-specimen/SpacingScale.tsx +33 -0
- package/src/ui/design-system/components/primer-specimen/SpecimenBoxRow.case.tsx +56 -0
- package/src/ui/design-system/components/primer-specimen/SpecimenBoxRow.placard.md +17 -0
- package/src/ui/design-system/components/primer-specimen/SpecimenBoxRow.tsx +45 -0
- package/src/ui/design-system/components/primer-specimen/StatusList.case.tsx +17 -0
- package/src/ui/design-system/components/primer-specimen/StatusList.placard.md +16 -0
- package/src/ui/design-system/components/primer-specimen/StatusList.tsx +39 -0
- package/src/ui/design-system/components/primer-specimen/SwatchGrid.case.tsx +26 -0
- package/src/ui/design-system/components/primer-specimen/SwatchGrid.placard.md +15 -0
- package/src/ui/design-system/components/primer-specimen/SwatchGrid.tsx +42 -0
- package/src/ui/design-system/components/primer-specimen/TypeScale.case.tsx +23 -0
- package/src/ui/design-system/components/primer-specimen/TypeScale.placard.md +14 -0
- package/src/ui/design-system/components/primer-specimen/TypeScale.tsx +34 -0
- package/src/ui/design-system/components/primer-specimen/WeightSpecimen.case.tsx +28 -0
- package/src/ui/design-system/components/primer-specimen/WeightSpecimen.placard.md +15 -0
- package/src/ui/design-system/components/primer-specimen/WeightSpecimen.tsx +46 -0
- package/src/ui/design-system/components/primer-specimen/index.ts +31 -0
- package/src/ui/design-system/components/primer-specimen/styles.css +476 -0
- package/src/ui/design-system/components/shell/A11yPage.case.tsx +237 -0
- package/src/ui/design-system/components/shell/A11yPage.placard.md +15 -0
- package/src/ui/design-system/components/shell/CaseTemplate.case.tsx +32 -0
- package/src/ui/design-system/components/shell/CaseTemplate.placard.md +5 -0
- package/src/ui/design-system/components/shell/CasesPage.case.tsx +141 -0
- package/src/ui/design-system/components/shell/CasesPage.placard.md +12 -0
- package/src/ui/design-system/components/shell/PrimerPage.case.tsx +22 -0
- package/src/ui/design-system/components/shell/PrimerPage.placard.md +3 -0
- package/src/ui/design-system/components/shell/PrimerTemplate.case.tsx +22 -0
- package/src/ui/design-system/components/shell/PrimerTemplate.placard.md +5 -0
- package/src/ui/design-system/components/shell/ShellView.case.tsx +57 -0
- package/src/ui/design-system/components/shell/ShellView.placard.md +5 -0
- package/src/ui/design-system/components/shell/ShellView.tsx +678 -0
- package/src/ui/design-system/components/shell/shell-fixtures.tsx +727 -0
- package/src/ui/design-system/components/showcase/A11yBadge.case.tsx +46 -0
- package/src/ui/design-system/components/showcase/A11yBadge.css +27 -0
- package/src/ui/design-system/components/showcase/A11yBadge.placard.md +11 -0
- package/src/ui/design-system/components/showcase/A11yBadge.test.tsx +31 -0
- package/src/ui/design-system/components/showcase/A11yBadge.tsx +41 -0
- package/src/ui/design-system/components/showcase/A11yPanel.case.tsx +121 -0
- package/src/ui/design-system/components/showcase/A11yPanel.css +198 -0
- package/src/ui/design-system/components/showcase/A11yPanel.placard.md +19 -0
- package/src/ui/design-system/components/showcase/A11yPanel.test.tsx +81 -0
- package/src/ui/design-system/components/showcase/A11yPanel.tsx +144 -0
- package/src/ui/design-system/components/showcase/Chip.case.tsx +48 -0
- package/src/ui/design-system/components/showcase/Chip.css +51 -0
- package/src/ui/design-system/components/showcase/Chip.placard.md +13 -0
- package/src/ui/design-system/components/showcase/Chip.test.tsx +46 -0
- package/src/ui/design-system/components/showcase/Chip.tsx +54 -0
- package/src/ui/design-system/components/showcase/Eyebrow.case.tsx +30 -0
- package/src/ui/design-system/components/showcase/Eyebrow.css +16 -0
- package/src/ui/design-system/components/showcase/Eyebrow.placard.md +10 -0
- package/src/ui/design-system/components/showcase/Eyebrow.test.tsx +38 -0
- package/src/ui/design-system/components/showcase/Eyebrow.tsx +29 -0
- package/src/ui/design-system/components/showcase/FlowNav.case.tsx +35 -0
- package/src/ui/design-system/components/showcase/FlowNav.css +29 -0
- package/src/ui/design-system/components/showcase/FlowNav.placard.md +13 -0
- package/src/ui/design-system/components/showcase/FlowNav.test.tsx +48 -0
- package/src/ui/design-system/components/showcase/FlowNav.tsx +58 -0
- package/src/ui/design-system/components/showcase/ImpactTag.case.tsx +19 -0
- package/src/ui/design-system/components/showcase/ImpactTag.css +36 -0
- package/src/ui/design-system/components/showcase/ImpactTag.placard.md +14 -0
- package/src/ui/design-system/components/showcase/ImpactTag.test.tsx +40 -0
- package/src/ui/design-system/components/showcase/ImpactTag.tsx +35 -0
- package/src/ui/design-system/components/showcase/NavItem.case.tsx +86 -0
- package/src/ui/design-system/components/showcase/NavItem.css +111 -0
- package/src/ui/design-system/components/showcase/NavItem.placard.md +13 -0
- package/src/ui/design-system/components/showcase/NavItem.test.tsx +65 -0
- package/src/ui/design-system/components/showcase/NavItem.tsx +95 -0
- package/src/ui/design-system/components/showcase/RenderAddress.case.tsx +21 -0
- package/src/ui/design-system/components/showcase/RenderAddress.css +35 -0
- package/src/ui/design-system/components/showcase/RenderAddress.placard.md +7 -0
- package/src/ui/design-system/components/showcase/RenderAddress.test.tsx +26 -0
- package/src/ui/design-system/components/showcase/RenderAddress.tsx +43 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.case.tsx +84 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.css +61 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.placard.md +21 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.test.tsx +81 -0
- package/src/ui/design-system/components/showcase/SegmentedToggle.tsx +75 -0
- package/src/ui/design-system/components/showcase/Sidebar.case.tsx +67 -0
- package/src/ui/design-system/components/showcase/Sidebar.css +6 -0
- package/src/ui/design-system/components/showcase/Sidebar.placard.md +14 -0
- package/src/ui/design-system/components/showcase/Sidebar.test.tsx +32 -0
- package/src/ui/design-system/components/showcase/Sidebar.tsx +30 -0
- package/src/ui/design-system/components/showcase/Stage.case.tsx +51 -0
- package/src/ui/design-system/components/showcase/Stage.css +91 -0
- package/src/ui/design-system/components/showcase/Stage.placard.md +15 -0
- package/src/ui/design-system/components/showcase/Stage.test.tsx +84 -0
- package/src/ui/design-system/components/showcase/Stage.tsx +97 -0
- package/src/ui/design-system/components/showcase/TweaksPanel.case.tsx +81 -0
- package/src/ui/design-system/components/showcase/TweaksPanel.css +169 -0
- package/src/ui/design-system/components/showcase/TweaksPanel.placard.md +20 -0
- package/src/ui/design-system/components/showcase/TweaksPanel.tsx +230 -0
- package/src/ui/design-system/components/showcase/Wordmark.case.tsx +42 -0
- package/src/ui/design-system/components/showcase/Wordmark.css +31 -0
- package/src/ui/design-system/components/showcase/Wordmark.placard.md +10 -0
- package/src/ui/design-system/components/showcase/Wordmark.test.tsx +22 -0
- package/src/ui/design-system/components/showcase/Wordmark.tsx +22 -0
- package/src/ui/design-system/primer-specimens/brand.tsx +26 -0
- package/src/ui/design-system/primer-specimens/colors.tsx +83 -0
- package/src/ui/design-system/primer-specimens/components.tsx +308 -0
- package/src/ui/design-system/primer-specimens/foundations.tsx +71 -0
- package/src/ui/design-system/primer-specimens/index.ts +25 -0
- package/src/ui/design-system/primer-specimens/showcase.tsx +68 -0
- package/src/ui/design-system/primer-specimens/spacing.tsx +101 -0
- package/src/ui/design-system/primer-specimens/type.tsx +75 -0
- package/src/ui/design-system/primer.mdx +236 -0
- package/src/ui/design-system/styles.css +14 -0
- package/src/ui/design-system/tokens/colors.css +172 -0
- package/src/ui/design-system/tokens/fonts.css +18 -0
- package/src/ui/design-system/tokens/spacing.css +48 -0
- package/src/ui/design-system/tokens/typography.css +49 -0
- package/src/ui/markdown.test.tsx +54 -0
- package/src/ui/markdown.tsx +19 -0
- package/src/ui/primer-mount.tsx +76 -0
- package/src/ui/primer.css +175 -0
- package/src/ui/primer.tsx +277 -0
- package/src/ui/render-mount.tsx +284 -0
- package/src/ui/shell-core.test.ts +340 -0
- package/src/ui/shell-core.ts +295 -0
- package/src/ui/shell.tsx +60 -0
- package/src/ui/test-ids.ts +53 -0
- package/src/ui/use-shell.ts +1230 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
Display Case — Spacing, radius, border & elevation tokens
|
|
3
|
+
Dense, rem-based spacing keeps the chrome compact so the
|
|
4
|
+
exhibit gets the room. Corners are modest (5px controls,
|
|
5
|
+
8px panels). Elevation is mostly border-led — the one place
|
|
6
|
+
we lift is floating overlays (menus, popovers).
|
|
7
|
+
============================================================ */
|
|
8
|
+
|
|
9
|
+
:root {
|
|
10
|
+
/* --- Spacing scale (rem) ----------------------------------- */
|
|
11
|
+
--dc-space-0: 0;
|
|
12
|
+
--dc-space-1: 0.125rem; /* 2px — icon gaps */
|
|
13
|
+
--dc-space-2: 0.25rem; /* 4px — control padding-y */
|
|
14
|
+
--dc-space-3: 0.375rem; /* 6px — nav-case padding */
|
|
15
|
+
--dc-space-4: 0.5rem; /* 8px — control padding-x, gaps */
|
|
16
|
+
--dc-space-5: 0.625rem; /* 10px — header padding-y */
|
|
17
|
+
--dc-space-6: 0.75rem; /* 12px — sidebar / panel padding */
|
|
18
|
+
--dc-space-8: 1rem; /* 16px — header padding-x, stage gap*/
|
|
19
|
+
--dc-space-10: 1.25rem; /* 20px — main padding */
|
|
20
|
+
--dc-space-12: 1.5rem; /* 24px */
|
|
21
|
+
--dc-space-16: 2rem; /* 32px — empty-state inset */
|
|
22
|
+
|
|
23
|
+
/* --- Layout sizes ------------------------------------------ */
|
|
24
|
+
--dc-sidebar-w: 15rem; /* fixed nav column */
|
|
25
|
+
--dc-doc-panel-w: 22rem; /* docs aside */
|
|
26
|
+
|
|
27
|
+
/* --- Radius (modest, precise) ------------------------------ */
|
|
28
|
+
--dc-radius-sm: 5px; /* buttons, inputs, chips, nav rows */
|
|
29
|
+
--dc-radius-md: 8px; /* panels, stage frame, doc panel */
|
|
30
|
+
--dc-radius-lg: 12px; /* overlays / large callouts */
|
|
31
|
+
--dc-radius-full: 999px;
|
|
32
|
+
|
|
33
|
+
/* --- Borders ----------------------------------------------- */
|
|
34
|
+
--dc-border-width: 1px;
|
|
35
|
+
--dc-border-line: var(--dc-border-width) solid var(--dc-border);
|
|
36
|
+
|
|
37
|
+
/* --- Elevation (rare — borders do most of the work) -------- */
|
|
38
|
+
--dc-shadow-none: none;
|
|
39
|
+
--dc-shadow-sm: 0 1px 2px rgba(33, 29, 24, 0.05);
|
|
40
|
+
--dc-shadow-overlay: 0 12px 32px -8px rgba(33, 29, 24, 0.22);
|
|
41
|
+
|
|
42
|
+
/* --- Motion ------------------------------------------------ */
|
|
43
|
+
--dc-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
|
|
44
|
+
--dc-duration-fast: 0.12s; /* chevron rotate, hover fills */
|
|
45
|
+
--dc-duration-base: 0.2s;
|
|
46
|
+
--dc-transition-fast: var(--dc-duration-fast) var(--dc-ease);
|
|
47
|
+
--dc-transition-base: var(--dc-duration-base) var(--dc-ease);
|
|
48
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
Display Case — Typography tokens
|
|
3
|
+
Hanken Grotesk drives the chrome (warm, legible, quiet).
|
|
4
|
+
JetBrains Mono carries developer texture — labels, code,
|
|
5
|
+
values, the wordmark. The chrome lives at small sizes so the
|
|
6
|
+
showcased component owns the visual weight.
|
|
7
|
+
============================================================ */
|
|
8
|
+
|
|
9
|
+
:root {
|
|
10
|
+
/* --- Families ---------------------------------------------- */
|
|
11
|
+
--dc-font-sans:
|
|
12
|
+
"Hanken Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
|
|
13
|
+
Roboto, sans-serif;
|
|
14
|
+
--dc-font-mono:
|
|
15
|
+
"JetBrains Mono", ui-monospace, "SF Mono", "Cascadia Code", Menlo, Consolas,
|
|
16
|
+
monospace;
|
|
17
|
+
|
|
18
|
+
/* --- Size scale (px; dense chrome) ------------------------- */
|
|
19
|
+
--dc-text-2xs: 10px; /* compact chrome — undocked tweaks */
|
|
20
|
+
--dc-text-xs: 11px; /* eyebrow labels, dim hints */
|
|
21
|
+
--dc-text-sm: 12px; /* zoom %, secondary, captions */
|
|
22
|
+
--dc-text-base: 14px; /* the chrome baseline */
|
|
23
|
+
--dc-text-md: 16px; /* icon glyphs, emphasis */
|
|
24
|
+
--dc-text-lg: 20px;
|
|
25
|
+
--dc-text-xl: 28px; /* doc panel headings */
|
|
26
|
+
--dc-text-2xl: 40px; /* wordmark / specimen display */
|
|
27
|
+
|
|
28
|
+
/* --- Weights ----------------------------------------------- */
|
|
29
|
+
--dc-weight-normal: 400;
|
|
30
|
+
--dc-weight-medium: 500; /* active nav, control labels */
|
|
31
|
+
--dc-weight-semibold: 600; /* title, group labels */
|
|
32
|
+
--dc-weight-bold: 700; /* wordmark, rare emphasis */
|
|
33
|
+
|
|
34
|
+
/* --- Line height ------------------------------------------- */
|
|
35
|
+
--dc-leading-tight: 1.2;
|
|
36
|
+
--dc-leading-normal: 1.45;
|
|
37
|
+
--dc-leading-relaxed: 1.6; /* doc / markdown copy */
|
|
38
|
+
|
|
39
|
+
/* --- Letter spacing ---------------------------------------- */
|
|
40
|
+
--dc-tracking-label: 0.08em; /* uppercase mono eyebrow labels */
|
|
41
|
+
--dc-tracking-tight: -0.01em; /* display / wordmark */
|
|
42
|
+
--dc-tracking-normal: 0;
|
|
43
|
+
|
|
44
|
+
/* --- Composite role tokens --------------------------------- */
|
|
45
|
+
/* Uppercase mono eyebrow — the signature label treatment. */
|
|
46
|
+
--dc-eyebrow:
|
|
47
|
+
var(--dc-weight-medium) var(--dc-text-xs) / var(--dc-leading-tight)
|
|
48
|
+
var(--dc-font-mono);
|
|
49
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { renderToStaticMarkup } from 'react-dom/server'
|
|
3
|
+
import { DocMarkdown } from './markdown'
|
|
4
|
+
|
|
5
|
+
describe('DocMarkdown', () => {
|
|
6
|
+
test('renders the placard CommonMark + GFM subset', () => {
|
|
7
|
+
const html = renderToStaticMarkup(
|
|
8
|
+
<DocMarkdown
|
|
9
|
+
source={[
|
|
10
|
+
'# Title',
|
|
11
|
+
'',
|
|
12
|
+
'A **bold** and *italic* word with `code`.',
|
|
13
|
+
'',
|
|
14
|
+
'- one',
|
|
15
|
+
'- two',
|
|
16
|
+
'',
|
|
17
|
+
'[link](https://example.com)',
|
|
18
|
+
'',
|
|
19
|
+
'```ts',
|
|
20
|
+
'const x = 1',
|
|
21
|
+
'```',
|
|
22
|
+
].join('\n')}
|
|
23
|
+
/>,
|
|
24
|
+
)
|
|
25
|
+
expect(html).toContain('class="dc-doc-md"')
|
|
26
|
+
expect(html).toMatch(/<h1[^>]*>Title<\/h1>/)
|
|
27
|
+
expect(html).toContain('<strong>bold</strong>')
|
|
28
|
+
expect(html).toContain('<em>italic</em>')
|
|
29
|
+
expect(html).toContain('<code>code</code>')
|
|
30
|
+
expect(html).toContain('<li>one</li>')
|
|
31
|
+
expect(html).toMatch(/<a[^>]*href="https:\/\/example\.com"/)
|
|
32
|
+
expect(html).toContain('<pre>')
|
|
33
|
+
expect(html).toContain('const x = 1')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('applies GFM extensions like tables and strikethrough', () => {
|
|
37
|
+
const html = renderToStaticMarkup(
|
|
38
|
+
<DocMarkdown source={'| A | B |\n| - | - |\n| 1 | 2 |\n\n~~gone~~'} />,
|
|
39
|
+
)
|
|
40
|
+
expect(html).toMatch(/<table/)
|
|
41
|
+
expect(html).toMatch(/<th[^>]*>A<\/th>/)
|
|
42
|
+
expect(html).toMatch(/<td[^>]*>1<\/td>/)
|
|
43
|
+
expect(html).toContain('<del>gone</del>')
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('does not inject raw HTML from the source into the chrome', () => {
|
|
47
|
+
const html = renderToStaticMarkup(
|
|
48
|
+
<DocMarkdown source={'<script>alert(1)</script>\n\n<b>x</b> text'} />,
|
|
49
|
+
)
|
|
50
|
+
// disableParsingRawHTML: tags are escaped to text, never live elements.
|
|
51
|
+
expect(html).not.toContain('<script>')
|
|
52
|
+
expect(html).not.toContain('<b>x</b>')
|
|
53
|
+
})
|
|
54
|
+
})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Markdown from 'markdown-to-jsx'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Renders a component's authored `.placard.md` as full CommonMark + GFM via
|
|
5
|
+
* `markdown-to-jsx` — a ~zero-dependency renderer that emits React elements
|
|
6
|
+
* (no `dangerouslySetInnerHTML`). Raw HTML is intentionally NOT rendered
|
|
7
|
+
* (`disableParsingRawHTML`), so a doc file can't inject markup into the chrome.
|
|
8
|
+
* Syntax highlighting is a deliberate non-goal — fenced code renders as plain
|
|
9
|
+
* styled <pre><code>.
|
|
10
|
+
*/
|
|
11
|
+
const OPTIONS = { disableParsingRawHTML: true } as const
|
|
12
|
+
|
|
13
|
+
export function DocMarkdown({ source }: { source: string }) {
|
|
14
|
+
return (
|
|
15
|
+
<div className="dc-doc-md">
|
|
16
|
+
<Markdown options={OPTIONS}>{source}</Markdown>
|
|
17
|
+
</div>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { ReactNode } from 'react'
|
|
2
|
+
import { StrictMode } from 'react'
|
|
3
|
+
import { createRoot, hydrateRoot } from 'react-dom/client'
|
|
4
|
+
import { PrimerRoot } from './primer'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Entry point for the isolated `/render/primer` document. Mounts the compiled MDX
|
|
8
|
+
* primer into #root and sets the initial theme from the URL (`?theme=`); later
|
|
9
|
+
* theme changes arrive over `postMessage` (handled in {@link PrimerRoot}).
|
|
10
|
+
*
|
|
11
|
+
* Driven two ways, mirroring the `/render` frame:
|
|
12
|
+
* - **Standalone** (direct navigation / snapshot): theme comes from the URL.
|
|
13
|
+
* - **Embedded** (browse chrome iframe): the chrome pushes theme + scroll
|
|
14
|
+
* messages and reads back the section list + active section.
|
|
15
|
+
*/
|
|
16
|
+
type MDXContent = (props: { components?: unknown }) => ReactNode
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Neutralize anchor clicks that would unload the primer frame. Authored prose
|
|
20
|
+
* can contain real `<a href>` links; in this isolated frame a click would do a
|
|
21
|
+
* full-document navigation and sever the chrome↔frame handshake. In-page hash
|
|
22
|
+
* links and `target=_blank` are harmless and left alone.
|
|
23
|
+
*/
|
|
24
|
+
function blockFrameNavigation(): void {
|
|
25
|
+
document.addEventListener(
|
|
26
|
+
'click',
|
|
27
|
+
(e) => {
|
|
28
|
+
if (
|
|
29
|
+
e.defaultPrevented ||
|
|
30
|
+
e.button !== 0 ||
|
|
31
|
+
e.metaKey ||
|
|
32
|
+
e.ctrlKey ||
|
|
33
|
+
e.shiftKey ||
|
|
34
|
+
e.altKey
|
|
35
|
+
) {
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
const anchor = (e.target as HTMLElement | null)?.closest?.('a')
|
|
39
|
+
const href = anchor?.getAttribute('href')
|
|
40
|
+
if (!anchor || !href) return
|
|
41
|
+
if (anchor.target && anchor.target !== '_self') return
|
|
42
|
+
const url = new URL(href, window.location.href)
|
|
43
|
+
if (url.pathname === window.location.pathname && url.hash !== '') return
|
|
44
|
+
e.preventDefault()
|
|
45
|
+
},
|
|
46
|
+
true,
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function mountPrimer(Content: MDXContent): void {
|
|
51
|
+
blockFrameNavigation()
|
|
52
|
+
const params = new URLSearchParams(window.location.search)
|
|
53
|
+
const theme = params.get('theme') === 'dark' ? 'dark' : 'light'
|
|
54
|
+
document.documentElement.dataset.theme = theme
|
|
55
|
+
document.documentElement.dataset.themePref = theme
|
|
56
|
+
|
|
57
|
+
const rootEl = document.getElementById('root') as HTMLElement
|
|
58
|
+
const tree = (
|
|
59
|
+
<StrictMode>
|
|
60
|
+
<PrimerRoot content={Content} />
|
|
61
|
+
</StrictMode>
|
|
62
|
+
)
|
|
63
|
+
// Adopt the server-rendered primer when present (`data-ssr="1"`); otherwise
|
|
64
|
+
// mount fresh (a browser-only specimen forced a client-render fallback).
|
|
65
|
+
if (rootEl.dataset.ssr === '1') {
|
|
66
|
+
hydrateRoot(rootEl, tree, {
|
|
67
|
+
onRecoverableError: (err) =>
|
|
68
|
+
console.warn(
|
|
69
|
+
'[display-case] primer adopt mismatch; client re-rendered:',
|
|
70
|
+
err,
|
|
71
|
+
),
|
|
72
|
+
})
|
|
73
|
+
} else {
|
|
74
|
+
createRoot(rootEl).render(tree)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
.dc-primer {
|
|
2
|
+
height: 100vh;
|
|
3
|
+
overflow-y: auto;
|
|
4
|
+
background: var(--dc-bg);
|
|
5
|
+
color: var(--dc-fg);
|
|
6
|
+
font-family: var(--dc-font-sans);
|
|
7
|
+
font-size: var(--dc-text-base);
|
|
8
|
+
line-height: var(--dc-leading-relaxed);
|
|
9
|
+
}
|
|
10
|
+
.dc-primer-inner {
|
|
11
|
+
max-width: 56rem;
|
|
12
|
+
margin: 0 auto;
|
|
13
|
+
padding: var(--dc-space-16) var(--dc-space-12) 6rem;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Prose — quiet, legible long-form copy. */
|
|
17
|
+
.dc-primer h1 {
|
|
18
|
+
font-size: var(--dc-text-2xl);
|
|
19
|
+
font-weight: var(--dc-weight-bold);
|
|
20
|
+
letter-spacing: var(--dc-tracking-tight);
|
|
21
|
+
line-height: var(--dc-leading-tight);
|
|
22
|
+
margin: 0 0 var(--dc-space-8);
|
|
23
|
+
}
|
|
24
|
+
.dc-primer h2 {
|
|
25
|
+
font-size: var(--dc-text-xl);
|
|
26
|
+
font-weight: var(--dc-weight-semibold);
|
|
27
|
+
letter-spacing: var(--dc-tracking-tight);
|
|
28
|
+
line-height: var(--dc-leading-tight);
|
|
29
|
+
margin: 3rem 0 var(--dc-space-8);
|
|
30
|
+
scroll-margin-top: var(--dc-space-8);
|
|
31
|
+
}
|
|
32
|
+
.dc-primer h3 {
|
|
33
|
+
font-size: var(--dc-text-lg);
|
|
34
|
+
font-weight: var(--dc-weight-semibold);
|
|
35
|
+
margin: var(--dc-space-12) 0 var(--dc-space-4);
|
|
36
|
+
}
|
|
37
|
+
.dc-primer p {
|
|
38
|
+
margin: 0 0 var(--dc-space-8);
|
|
39
|
+
color: var(--dc-fg);
|
|
40
|
+
}
|
|
41
|
+
.dc-primer a {
|
|
42
|
+
color: var(--dc-brand);
|
|
43
|
+
text-decoration: none;
|
|
44
|
+
}
|
|
45
|
+
.dc-primer a:hover {
|
|
46
|
+
text-decoration: underline;
|
|
47
|
+
}
|
|
48
|
+
.dc-primer strong {
|
|
49
|
+
font-weight: var(--dc-weight-semibold);
|
|
50
|
+
}
|
|
51
|
+
.dc-primer ul,
|
|
52
|
+
.dc-primer ol {
|
|
53
|
+
margin: 0 0 var(--dc-space-8);
|
|
54
|
+
padding-left: var(--dc-space-12);
|
|
55
|
+
}
|
|
56
|
+
.dc-primer li {
|
|
57
|
+
margin: var(--dc-space-2) 0;
|
|
58
|
+
}
|
|
59
|
+
.dc-primer code {
|
|
60
|
+
font-family: var(--dc-font-mono);
|
|
61
|
+
font-size: 0.9em;
|
|
62
|
+
background: var(--dc-bg-subtle);
|
|
63
|
+
border: var(--dc-border-line);
|
|
64
|
+
border-radius: var(--dc-radius-sm);
|
|
65
|
+
padding: 0.05em 0.35em;
|
|
66
|
+
}
|
|
67
|
+
.dc-primer pre {
|
|
68
|
+
font-family: var(--dc-font-mono);
|
|
69
|
+
font-size: var(--dc-text-sm);
|
|
70
|
+
line-height: var(--dc-leading-normal);
|
|
71
|
+
background: var(--dc-bg-subtle);
|
|
72
|
+
border: var(--dc-border-line);
|
|
73
|
+
border-radius: var(--dc-radius-md);
|
|
74
|
+
padding: var(--dc-space-8);
|
|
75
|
+
overflow-x: auto;
|
|
76
|
+
margin: 0 0 var(--dc-space-8);
|
|
77
|
+
}
|
|
78
|
+
.dc-primer pre code {
|
|
79
|
+
background: none;
|
|
80
|
+
border: 0;
|
|
81
|
+
padding: 0;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Tables (GFM) — border-led and flat, like every other Vitrine surface: a
|
|
85
|
+
hairline frame, a quiet mono header on the subtle fill, and hairline row
|
|
86
|
+
rules. Authored as Markdown pipe-tables; this gives them the chrome's look
|
|
87
|
+
without per-document styling. */
|
|
88
|
+
.dc-primer table {
|
|
89
|
+
width: 100%;
|
|
90
|
+
border-collapse: collapse;
|
|
91
|
+
margin: 0 0 var(--dc-space-8);
|
|
92
|
+
font-size: var(--dc-text-sm);
|
|
93
|
+
border: var(--dc-border-line);
|
|
94
|
+
}
|
|
95
|
+
.dc-primer thead th {
|
|
96
|
+
text-align: left;
|
|
97
|
+
padding: var(--dc-space-5) var(--dc-space-6);
|
|
98
|
+
background: var(--dc-bg-subtle);
|
|
99
|
+
color: var(--dc-fg-muted);
|
|
100
|
+
font-family: var(--dc-font-mono);
|
|
101
|
+
font-size: var(--dc-text-2xs);
|
|
102
|
+
font-weight: var(--dc-weight-medium);
|
|
103
|
+
text-transform: uppercase;
|
|
104
|
+
letter-spacing: 0.05em;
|
|
105
|
+
border-bottom: var(--dc-border-line);
|
|
106
|
+
}
|
|
107
|
+
.dc-primer tbody td {
|
|
108
|
+
padding: var(--dc-space-5) var(--dc-space-6);
|
|
109
|
+
vertical-align: top;
|
|
110
|
+
color: var(--dc-fg);
|
|
111
|
+
}
|
|
112
|
+
.dc-primer tbody tr + tr td {
|
|
113
|
+
border-top: var(--dc-border-line);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* Display — the specimen card contract. The title and subtitle sit as plain
|
|
117
|
+
wall-text *above* the box; the box holds only the live specimen. */
|
|
118
|
+
.dc-display {
|
|
119
|
+
margin: 0 0 var(--dc-space-12);
|
|
120
|
+
scroll-margin-top: var(--dc-space-8);
|
|
121
|
+
}
|
|
122
|
+
.dc-display-head {
|
|
123
|
+
margin-bottom: var(--dc-space-5);
|
|
124
|
+
}
|
|
125
|
+
.dc-display-title {
|
|
126
|
+
font-size: var(--dc-text-lg);
|
|
127
|
+
font-weight: var(--dc-weight-semibold);
|
|
128
|
+
letter-spacing: var(--dc-tracking-tight);
|
|
129
|
+
line-height: var(--dc-leading-tight);
|
|
130
|
+
color: var(--dc-fg);
|
|
131
|
+
}
|
|
132
|
+
.dc-display-sub {
|
|
133
|
+
margin-top: var(--dc-space-1);
|
|
134
|
+
font-size: var(--dc-text-sm);
|
|
135
|
+
color: var(--dc-fg-muted);
|
|
136
|
+
}
|
|
137
|
+
.dc-display-specimen {
|
|
138
|
+
border: var(--dc-border-line);
|
|
139
|
+
border-radius: var(--dc-radius-md);
|
|
140
|
+
background: var(--dc-bg);
|
|
141
|
+
padding: var(--dc-space-12);
|
|
142
|
+
display: flex;
|
|
143
|
+
flex-wrap: wrap;
|
|
144
|
+
gap: var(--dc-space-8);
|
|
145
|
+
align-items: center;
|
|
146
|
+
justify-content: center;
|
|
147
|
+
}
|
|
148
|
+
/* A forced-theme specimen re-scopes the design tokens for its subtree, so it
|
|
149
|
+
must repaint its own surface (the value the consumer app would give it). */
|
|
150
|
+
.dc-display-specimen[data-theme] {
|
|
151
|
+
background: var(--dc-bg);
|
|
152
|
+
color: var(--dc-fg);
|
|
153
|
+
}
|
|
154
|
+
/* App surface — opt in (appSurface) to paint the consumer design system's own
|
|
155
|
+
canvas (--color-bg/--color-fg, the same tokens the /render frame paints)
|
|
156
|
+
instead of the Vitrine's --dc-bg, so a specimen sits on the exact background
|
|
157
|
+
the real app gives it. Degrades to --dc-bg when the consumer defines no
|
|
158
|
+
--color-bg. Listed after the [data-theme] rule (equal specificity → source
|
|
159
|
+
order wins), and the [data-theme] combination is spelled out so a forced-theme
|
|
160
|
+
app-surface specimen resolves the themed --color-bg of its own subtree. */
|
|
161
|
+
.dc-display-specimen[data-app-surface],
|
|
162
|
+
.dc-display-specimen[data-app-surface][data-theme] {
|
|
163
|
+
background: var(--color-bg, var(--dc-bg));
|
|
164
|
+
color: var(--color-fg, var(--dc-fg));
|
|
165
|
+
}
|
|
166
|
+
/* Flush — the specimen draws no frame or padding of its own. A single
|
|
167
|
+
self-bordered child (e.g. a DefinitionList) fills the box edge-to-edge and
|
|
168
|
+
supplies the one border, so there's no box-within-a-box; the rounded, clipped
|
|
169
|
+
background still backs the child's corners. */
|
|
170
|
+
.dc-display-specimen[data-flush] {
|
|
171
|
+
display: block;
|
|
172
|
+
padding: 0;
|
|
173
|
+
border: 0;
|
|
174
|
+
overflow: hidden;
|
|
175
|
+
}
|