@master/internal-site 0.1.2 → 0.2.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.
Files changed (239) hide show
  1. package/README.md +43 -0
  2. package/dist/astro.mjs +233 -47
  3. package/dist/config.d.mts +6 -0
  4. package/dist/localization-LfIbIysE.mjs +356 -0
  5. package/dist/localization.d.mts +69 -0
  6. package/dist/localization.mjs +1 -0
  7. package/package.json +12 -9
  8. package/src/astro/content-pages.ts +30 -11
  9. package/src/astro/index.ts +11 -5
  10. package/src/astro/localized-mdx.ts +45 -0
  11. package/src/astro/routes.template.ts +4 -0
  12. package/src/astro/site-context.ts +10 -2
  13. package/src/astro/style-assets.ts +107 -0
  14. package/src/benchmarks/BenchmarkBarRows.astro +3 -2
  15. package/src/benchmarks/BenchmarkBars.astro +1 -1
  16. package/src/benchmarks/BenchmarkChart.astro +5 -1
  17. package/src/benchmarks/BenchmarkChartGroup.astro +1 -1
  18. package/src/benchmarks/BenchmarkDataTable.astro +1 -1
  19. package/src/benchmarks/BenchmarkFigure.astro +2 -1
  20. package/src/benchmarks/BenchmarkMetricTable.astro +2 -2
  21. package/src/benchmarks/BenchmarkMetrics.astro +1 -1
  22. package/src/benchmarks/BenchmarkSampleSummary.astro +1 -1
  23. package/src/benchmarks/BenchmarkScrollRegion.svelte +2 -1
  24. package/src/benchmarks/BenchmarkSource.astro +1 -1
  25. package/src/benchmarks/BenchmarkStackedBars.astro +4 -3
  26. package/src/benchmarks/BenchmarkStatus.astro +3 -1
  27. package/src/benchmarks/localization.ts +22 -0
  28. package/src/benchmarks/types.ts +2 -0
  29. package/src/blog/article/BlogArticleContents.svelte +1 -1
  30. package/src/blog/article/BlogArticleEnd.astro +6 -9
  31. package/src/blog/article/BlogArticleMasthead.astro +17 -9
  32. package/src/config.ts +3 -0
  33. package/src/css/NamespaceUtilityTable.astro +1 -1
  34. package/src/css/ThemeNumberVariableTable.astro +1 -1
  35. package/src/css/foundation-data/specimen-content.ts +2 -2
  36. package/src/css/markdown.ts +64 -14
  37. package/src/demo/Demo.svelte +10 -4
  38. package/src/demo/DemoAsset.astro +6 -3
  39. package/src/demo/DemoAxes.svelte +4 -2
  40. package/src/demo/DemoCatalog.astro +9 -6
  41. package/src/demo/DemoComparison.svelte +3 -0
  42. package/src/demo/DemoContainer.svelte +8 -3
  43. package/src/demo/DemoControls.svelte +5 -1
  44. package/src/demo/DemoCopyButton.svelte +9 -4
  45. package/src/demo/DemoCopyGroup.svelte +6 -2
  46. package/src/demo/DemoExample.astro +3 -1
  47. package/src/demo/DemoFeatureSupport.svelte +5 -1
  48. package/src/demo/DemoGeneratedCSS.svelte +5 -1
  49. package/src/demo/DemoIndex.astro +6 -3
  50. package/src/demo/DemoLegend.svelte +3 -1
  51. package/src/demo/DemoMeasure.svelte +4 -2
  52. package/src/demo/DemoMedia.svelte +3 -1
  53. package/src/demo/DemoMotion.svelte +6 -3
  54. package/src/demo/DemoPaletteGroups.svelte +4 -2
  55. package/src/demo/DemoScrollArea.svelte +2 -0
  56. package/src/demo/DemoStyleComparison.astro +6 -3
  57. package/src/demo/DemoSurface.svelte +2 -0
  58. package/src/demo/DemoThemeComparison.astro +5 -2
  59. package/src/demo/DemoTokenTable.astro +5 -3
  60. package/src/demo/DemoViewTransition.astro +3 -1
  61. package/src/demo/DemoViewport.svelte +18 -11
  62. package/src/demo/DemoWaterfall.astro +4 -2
  63. package/src/demo/ProjectStyleExample.astro +4 -1
  64. package/src/demo/foundations/FoundationDialog.astro +6 -2
  65. package/src/demo/foundations/FoundationMedia.astro +1 -0
  66. package/src/demo/foundations/FoundationShrink.astro +1 -0
  67. package/src/demo/foundations/FoundationTokensView.astro +16 -5
  68. package/src/demo/foundations/Palette.svelte +11 -7
  69. package/src/demo/foundations/dialog-copy.d.ts +1 -0
  70. package/src/demo/foundations/dialog-copy.js +5 -0
  71. package/src/demo/illustration-materials.ts +1 -1
  72. package/src/demo/illustration.css +218 -0
  73. package/src/docs-shell/common/i18n.config.d.ts +1 -1
  74. package/src/docs-shell/common/i18n.config.js +4 -12
  75. package/src/docs-shell/common/locales.d.ts +8 -0
  76. package/src/docs-shell/common/locales.js +18 -0
  77. package/src/docs-shell/common/terminology.json +32 -0
  78. package/src/docs-shell/common/translations.d.ts +3 -0
  79. package/src/docs-shell/common/translations.js +294 -0
  80. package/src/docs-shell/components/ArticleHeader.astro +5 -3
  81. package/src/docs-shell/components/Callout.astro +1 -0
  82. package/src/docs-shell/components/CodeCopyButton.astro +5 -1
  83. package/src/docs-shell/components/CodeTabBar.astro +7 -3
  84. package/src/docs-shell/components/CodeTabs.astro +2 -1
  85. package/src/docs-shell/components/CodeView.astro +1 -1
  86. package/src/docs-shell/components/DocHeader.svelte +13 -17
  87. package/src/docs-shell/components/DocHeading.astro +3 -1
  88. package/src/docs-shell/components/DocMenuButton.svelte +126 -61
  89. package/src/docs-shell/components/DocSidebar.svelte +3 -2
  90. package/src/docs-shell/components/DocVersionSelect.svelte +2 -0
  91. package/src/docs-shell/components/DocumentAPIIndex.astro +1 -1
  92. package/src/docs-shell/components/DocumentChoices.astro +1 -1
  93. package/src/docs-shell/components/DocumentCodeExampleView.astro +3 -1
  94. package/src/docs-shell/components/DocumentCodeTable.astro +4 -2
  95. package/src/docs-shell/components/DocumentCopyButton.svelte +3 -0
  96. package/src/docs-shell/components/DocumentDisclosure.astro +2 -0
  97. package/src/docs-shell/components/DocumentFileTree.astro +3 -0
  98. package/src/docs-shell/components/DocumentFlow.astro +4 -1
  99. package/src/docs-shell/components/DocumentIllustration.astro +8 -12
  100. package/src/docs-shell/components/DocumentKeyList.astro +6 -4
  101. package/src/docs-shell/components/DocumentNamespaceTable.astro +5 -3
  102. package/src/docs-shell/components/DocumentOptionEntry.astro +0 -1
  103. package/src/docs-shell/components/DocumentOptionList.astro +1 -1
  104. package/src/docs-shell/components/DocumentOptions.astro +3 -2
  105. package/src/docs-shell/components/DocumentParameters.astro +1 -1
  106. package/src/docs-shell/components/DocumentPrompt.astro +2 -0
  107. package/src/docs-shell/components/DocumentValueList.astro +1 -1
  108. package/src/docs-shell/components/DocumentationIndex.svelte +8 -7
  109. package/src/docs-shell/components/ExpandContent.svelte +3 -0
  110. package/src/docs-shell/components/ExplorerView.astro +1 -0
  111. package/src/docs-shell/components/GeneratedCSS.astro +4 -1
  112. package/src/docs-shell/components/Header.svelte +1 -1
  113. package/src/docs-shell/components/InlineCode.astro +1 -0
  114. package/src/docs-shell/components/InteractingIndicator.astro +1 -0
  115. package/src/docs-shell/components/LanguageButton.svelte +1 -0
  116. package/src/docs-shell/components/LanguageSelect.svelte +2 -0
  117. package/src/docs-shell/components/Link.svelte +3 -2
  118. package/src/docs-shell/components/NotFound.astro +6 -4
  119. package/src/docs-shell/components/PMAction.astro +1 -0
  120. package/src/docs-shell/components/PageContent.svelte +1 -1
  121. package/src/docs-shell/components/PageContentDisclosure.astro +2 -1
  122. package/src/docs-shell/components/PageContentList.svelte +2 -0
  123. package/src/docs-shell/components/PageNavs.astro +3 -3
  124. package/src/docs-shell/components/SearchButton.svelte +3 -0
  125. package/src/docs-shell/components/SearchDialog.svelte +18 -16
  126. package/src/docs-shell/components/Segments.svelte +2 -0
  127. package/src/docs-shell/components/StepSection.astro +1 -2
  128. package/src/docs-shell/components/Tabs.astro +1 -0
  129. package/src/docs-shell/components/ThemeSelect.svelte +2 -0
  130. package/src/docs-shell/components/TimeAgo.svelte +6 -6
  131. package/src/docs-shell/components/markdown/Table.astro +1 -0
  132. package/src/docs-shell/components/markdown-components.ts +0 -1
  133. package/src/docs-shell/contexts/search.ts +1 -1
  134. package/src/docs-shell/factories/Page.astro +10 -5
  135. package/src/docs-shell/factories/create-page.ts +33 -6
  136. package/src/docs-shell/layouts/BlogArticle.astro +3 -11
  137. package/src/docs-shell/remark/markdown-components.js +28 -7
  138. package/src/docs-shell/remark/slug-and-toc.js +5 -7
  139. package/src/docs-shell/scripts/code.ts +5 -2
  140. package/src/docs-shell/scripts/document.ts +1 -1
  141. package/src/docs-shell/site.ts +9 -1
  142. package/src/docs-shell/types/Metadata.ts +11 -0
  143. package/src/docs-shell/utils/document-copy.d.ts +2 -0
  144. package/src/docs-shell/utils/document-copy.js +18 -0
  145. package/src/docs-shell/utils/generate-metadata.ts +5 -1
  146. package/src/docs-shell/utils/head.ts +5 -2
  147. package/src/docs-shell/utils/i18n-pathname.ts +13 -2
  148. package/src/docs-shell/utils/i18n.ts +4 -1
  149. package/src/docs-shell/utils/localized-metadata.ts +22 -0
  150. package/src/docs-shell/utils/markdown-links.ts +61 -0
  151. package/src/docs-shell/utils/markdown-tree.ts +19 -6
  152. package/src/docs-shell/utils/resolve-heading.d.ts +1 -0
  153. package/src/docs-shell/utils/resolve-heading.js +21 -0
  154. package/src/docs-shell/utils/search-pages.ts +75 -22
  155. package/src/examples/components/ArticleTransitionScene.svelte +4 -0
  156. package/src/examples/layout-system/page.astro +0 -1
  157. package/src/home/HomeBentoFrame.astro +1 -0
  158. package/src/home/HomeBentoGrid.astro +1 -0
  159. package/src/home/bento.css +167 -0
  160. package/src/home/design.css +1 -1
  161. package/src/home/hero.css +5 -0
  162. package/src/home/home.css +4 -576
  163. package/src/home/segmented-control.css +94 -0
  164. package/src/layouts/Document.astro +4 -2
  165. package/src/public/icons/json.svg +1 -0
  166. package/src/scripts/atomic-class-authoring.ts +98 -0
  167. package/src/scripts/localization.ts +239 -0
  168. package/src/scripts/mdx-sample.ts +12 -0
  169. package/src/scripts/static-social.ts +17 -3
  170. package/src/scripts/style-delivery.mjs +120 -0
  171. package/src/sections/blog.astro +1 -1
  172. package/src/sections/guide.astro +4 -3
  173. package/src/styles/blog-article.css +10 -7
  174. package/src/styles/blog-materials.css +13 -0
  175. package/src/styles/demo.css +0 -1
  176. package/src/styles/docs-shell/code.css +5 -61
  177. package/src/styles/docs-shell/documentation-search.css +1 -0
  178. package/src/styles/docs-shell/header.css +2 -73
  179. package/src/styles/docs-shell/menu.css +37 -17
  180. package/src/styles/docs-shell/page-content.css +2 -37
  181. package/src/styles/docs-shell/prose.css +18 -9
  182. package/src/styles/docs-shell/steps.css +2 -39
  183. package/src/styles/document-comparison.css +5 -2
  184. package/src/styles/documentation-illustration.css +1 -9
  185. package/src/styles/documentation-index.css +2 -1
  186. package/src/styles/documents.css +2 -4
  187. package/src/styles/interface.css +5 -3
  188. package/src/styles/system/DESIGN.md +34 -14
  189. package/src/styles/system/browser.css +21 -0
  190. package/src/styles/system/button-feedback.css +13 -0
  191. package/src/styles/system/callout.css +14 -0
  192. package/src/styles/system/chart.css +18 -0
  193. package/src/styles/system/checks.css +18 -0
  194. package/src/styles/system/code-panel.css +82 -0
  195. package/src/styles/system/code-tabs.css +40 -0
  196. package/src/styles/system/code.css +8 -85
  197. package/src/styles/system/compare.css +7 -0
  198. package/src/styles/system/controls.css +10 -109
  199. package/src/styles/system/copy.css +32 -0
  200. package/src/styles/system/data.css +10 -134
  201. package/src/styles/system/dialog.css +16 -0
  202. package/src/styles/system/disclosure.css +19 -0
  203. package/src/styles/system/expand.css +11 -0
  204. package/src/styles/system/fields.css +25 -0
  205. package/src/styles/system/header-core.css +101 -0
  206. package/src/styles/system/hint.css +5 -0
  207. package/src/styles/system/index.css +13 -0
  208. package/src/styles/system/inline-code.css +4 -0
  209. package/src/styles/system/kbd.css +4 -0
  210. package/src/styles/system/logos.css +10 -0
  211. package/src/styles/system/menu.css +14 -0
  212. package/src/styles/system/metrics.css +22 -0
  213. package/src/styles/system/pagehead.css +13 -0
  214. package/src/styles/system/pager.css +13 -0
  215. package/src/styles/system/parameters.css +17 -0
  216. package/src/styles/system/prompt.css +7 -0
  217. package/src/styles/system/prose.css +4 -75
  218. package/src/styles/system/range.css +12 -0
  219. package/src/styles/system/reading.css +44 -0
  220. package/src/styles/system/scroll.css +6 -0
  221. package/src/styles/system/search-trigger.css +10 -0
  222. package/src/styles/system/segmented.css +22 -0
  223. package/src/styles/system/sheet.css +8 -0
  224. package/src/styles/system/shell.css +8 -102
  225. package/src/styles/system/sidebar.css +14 -0
  226. package/src/styles/system/source.css +5 -0
  227. package/src/styles/system/specimens.css +3 -0
  228. package/src/styles/system/stage.css +18 -0
  229. package/src/styles/system/steps.css +51 -0
  230. package/src/styles/system/surfaces.css +8 -104
  231. package/src/styles/system/table.css +23 -0
  232. package/src/styles/system/toc.css +59 -0
  233. package/src/styles/system/tooltip.css +6 -0
  234. package/src/styles/system/tray-code.css +6 -0
  235. package/src/styles/system/tray.css +20 -0
  236. package/src/styles/system/tree.css +15 -0
  237. package/src/styles/system/waterfall.css +14 -0
  238. package/src/virtual.d.ts +5 -1
  239. package/src/blog/article/language.ts +0 -29
@@ -6,6 +6,10 @@ import { mdxjsEsmFromMarkdown } from 'mdast-util-mdxjs-esm'
6
6
  import { mdxJsx } from 'micromark-extension-mdx-jsx'
7
7
  import { mdxJsxFromMarkdown } from 'mdast-util-mdx-jsx'
8
8
  import * as acorn from 'acorn'
9
+ import { parseFrontmatter } from '@astrojs/markdown-remark'
10
+ import { headingAnchors } from '../scripts/localization'
11
+ import { isSharedMdxSample } from '../scripts/mdx-sample'
12
+ import { translateShell } from '../docs-shell/common/translations'
9
13
  import { generatePresetCSS } from '@master/internal-site/css/generate-preset-css.ts'
10
14
  import { selectedSpecimensMarkdown } from '@master/internal-site/css/foundation-data/specimen-content.ts'
11
15
  import { getVariableNamespacePublicKeys } from '@master/internal-site/css/manifest-utilities.ts'
@@ -33,7 +37,7 @@ export function syntaxMarkdown(rows: SyntaxRow[]) {
33
37
  }
34
38
 
35
39
  export function portableMarkdown(markdown: string) {
36
- return markdown.split(/(```[\s\S]*?```)/g).map(part => part.startsWith('```') ? part : part.replace(/<!-- \/?reference-illustration(?::[\w-]+)? -->\n*/g, '').replace(/^(#{2,3}) (.+) \{#([\w-]+)\}$/gm, '<a id="$3"></a>\n\n$1 $2')).join('')
40
+ return markdown.split(/(```[\s\S]*?```)/g).map(part => part.startsWith('```') ? part : part.replace(/<!-- \/?reference-illustration(?::[\w-]+)? -->\n*/g, '').replace(/^(#{2,6}) (.+) \{#([\w-]+)\}$/gm, '<a id="$3"></a>\n\n$1 $2')).join('')
37
41
  }
38
42
 
39
43
  /** Resolve authored literals only. Never execute MDX expressions to extract documents. */
@@ -50,9 +54,11 @@ function literal(node: any, variables: Record<string, unknown>): unknown {
50
54
  }
51
55
 
52
56
  export interface MdxContentOptions {
57
+ /** Propagates through nested MDX includes and custom text adapters. */
58
+ locale?: string
53
59
  /** Reference Overview renders syntax rows; Guide Overview may be a local MDX include. */
54
60
  overview?: 'syntax' | 'include'
55
- component?: (name: string, attributes: Record<string, any>, file: string, context: { expression: (source: string) => Promise<unknown> }) => string | undefined | Promise<string | undefined>
61
+ component?: (name: string, attributes: Record<string, any>, file: string, context: { expression: (source: string) => Promise<unknown>; locale: string }) => string | undefined | Promise<string | undefined>
56
62
  }
57
63
 
58
64
  /**
@@ -64,7 +70,18 @@ const expressionElement = /(<(Code|Class2CSS|DocumentDeclaration)\b(?:"[^"]*"|'[
64
70
  export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [], stack: string[] = [], options: MdxContentOptions = {}) {
65
71
  if (stack.includes(file)) throw new Error(`Recursive document include: ${file}`)
66
72
  const expressions: Record<string, string> = {}
67
- const source = (await readFile(file, 'utf8')).split(/(```[\s\S]*?```)/g).map(part => part.startsWith('```') ? part : part.replace(/\{?<Demo\b[\s\S]*?<\/Demo>\}?/g, '').replace(expressionElement, (_, start, _name, expression, end) => { const key = `MCSS_EXPRESSION_${Object.keys(expressions).length}`; expressions[key] = expression; return `${start}\n${key}\n${end}` })).join('')
73
+ const locale = options.locale ?? file.match(/\.(tw|ja|ko|cn)\.mdx$/)?.[1] ?? 'en'
74
+ const t = (text: string) => translateShell(locale, text)
75
+ options = { ...options, locale }
76
+ const original = file.replace(/\.(tw|ja|ko|cn)\.mdx$/, '.mdx')
77
+ const canonical = parseFrontmatter(await readFile(original, 'utf8'))
78
+ if (canonical.frontmatter.draft === true || canonical.frontmatter.disabled === true) throw new Error(`Unpublished source MDX document: ${original}`)
79
+ const document = original === file ? canonical : parseFrontmatter(await readFile(file, 'utf8'))
80
+ if (document.frontmatter.draft === true || document.frontmatter.disabled === true) throw new Error(`Unpublished translated MDX document: ${file}`)
81
+ const source = document.content.split(/(```[\s\S]*?```)/g).map(part => part.startsWith('```') ? part : part.replace(/\{?<Demo\b[\s\S]*?<\/Demo>\}?/g, '').replace(expressionElement, (_, start, _name, expression, end) => { const key = `MCSS_EXPRESSION_${Object.keys(expressions).length}`; expressions[key] = expression; return `${start}\n${key}\n${end}` })).join('')
82
+ let sourceAnchors: string[]
83
+ try { sourceAnchors = headingAnchors(source) } catch (error) { throw new Error(`Cannot read headings in ${file}`, { cause: error }) }
84
+ const headingIds = new Map<any, string>()
68
85
  const examples: ReferenceExample[] = []
69
86
  const notes: string[] = []
70
87
  const variables: Record<string, unknown> = {}
@@ -75,6 +92,12 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
75
92
  extensions: [{ disable: { null: ['htmlFlow', 'htmlText', 'codeIndented'] } }, mdxjsEsm({ acorn, addResult: true }), mdxJsx({ acorn, addResult: true })],
76
93
  mdastExtensions: [mdxjsEsmFromMarkdown(), mdxJsxFromMarkdown()]
77
94
  }) } catch (error) { throw new Error(`Cannot parse ${file}`, { cause: error }) }
95
+ let headingIndex = 0
96
+ const collectHeadingIds = (node: any) => {
97
+ if (node.type === 'heading' && node.depth >= 2 && node.depth <= 6) headingIds.set(node, sourceAnchors[headingIndex++])
98
+ node.children?.forEach(collectHeadingIds)
99
+ }
100
+ collectHeadingIds(tree)
78
101
  const raw = (node: any) => source.slice(node.position.start.offset, node.position.end.offset)
79
102
  const collectEsm = (node: any) => {
80
103
  if (node.type === 'mdxjsEsm') {
@@ -135,7 +158,7 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
135
158
  const name = node.name ?? ''
136
159
  const attrs: Record<string, any> = Object.fromEntries((node.attributes ?? []).filter((a: any) => a.name).map((a: any) => [a.name, a.value]))
137
160
  const inner = (node.children ?? []).map(raw).join('\n')
138
- const adapted = await options.component?.(name, attrs, file, { expression })
161
+ const adapted = await options.component?.(name, attrs, file, { expression, locale })
139
162
  if (adapted !== undefined) return adapted
140
163
  const attribute = (value: any) => value?.type === 'mdxJsxAttributeValueExpression' ? expression(value.value) : value
141
164
  if (name === 'DocumentPrompt') {
@@ -148,10 +171,11 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
148
171
  const result = await attribute(attrs.result)
149
172
  const diagnostic = await attribute(attrs.diagnostic)
150
173
  if (typeof source !== 'string' || typeof attrs.language !== 'string') throw new Error('DocumentCodeExample requires literal source and language')
151
- const parts = [`**${attrs.title}**`, `\`\`\`${attrs.language}\n${source}\n\`\`\``]
174
+ const sourceLabel = attrs.sourceLabel ?? (result !== undefined && attrs.resultKind !== 'generated-css' ? t('Source') : undefined)
175
+ const parts = [`**${attrs.title}**`, ...(sourceLabel ? [`**${sourceLabel}**`] : []), `\`\`\`${attrs.language}\n${source}\n\`\`\``]
152
176
  if (result !== undefined) {
153
177
  if (typeof result !== 'string') throw new Error('DocumentCodeExample requires a literal result')
154
- parts.push(`**${attrs.resultLabel ?? (attrs.resultKind === 'generated-css' ? 'Generated CSS' : 'Result')}**`, `\`\`\`${attrs.resultLanguage ?? attrs.language}\n${result}\n\`\`\``)
178
+ parts.push(`**${attrs.resultLabel ?? t(attrs.resultKind === 'generated-css' ? 'Generated CSS' : 'Result')}**`, `\`\`\`${attrs.resultLanguage ?? attrs.language}\n${result}\n\`\`\``)
155
179
  }
156
180
  if (diagnostic) parts.push(`${diagnostic.severity} (\`${diagnostic.rule}\`): ${diagnostic.message}`)
157
181
  return parts.join('\n\n')
@@ -159,7 +183,7 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
159
183
  if (['DocumentOptions', 'DocumentParameters'].includes(name)) {
160
184
  const values = await expression(attrs[name === 'DocumentOptions' ? 'options' : 'parameters'].value) as { name: string, type?: string, requirement?: string, defaultValue?: string, description: string }[]
161
185
  if (!Array.isArray(values) || values.some(value => typeof value.name !== 'string' || typeof value.description !== 'string')) throw new Error(`${name} requires literal names and descriptions`)
162
- return values.map(value => `**\`${value.name}\`**${value.type ? ` — \`${value.type}\`` : ''}${value.requirement ? ` (${value.requirement})` : ''}\n\n${value.defaultValue !== undefined ? `Default: \`${value.defaultValue}\`. ` : ''}${value.description}`).join('\n\n')
186
+ return values.map(value => `**\`${value.name}\`**${value.type ? ` — \`${value.type}\`` : ''}${value.requirement ? ` (${value.requirement})` : ''}\n\n${value.defaultValue !== undefined ? `${t('Default')}: \`${value.defaultValue}\`. ` : ''}${value.description}`).join('\n\n')
163
187
  }
164
188
  if (name === 'DocumentAPIIndex') {
165
189
  const values = await expression(attrs.entries.value) as { name: string, href: string, description: string }[]
@@ -169,17 +193,25 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
169
193
  if (name === 'DocumentDeclaration') return `**${attrs.label}**\n\n\`\`\`typescript\n${await expression(inner)}\n\`\`\``
170
194
  if (name === 'DocumentValueList') {
171
195
  const values = await expression(attrs.rows.value) as { id: string, title: string, identifier?: string, values: { label: string, value: string }[] }[]
172
- return values.map(value => `### ${value.title} {#${value.id}}\n\n${value.identifier ? `\`${value.identifier}\`\n\n` : ''}${value.values.map(pair => `${pair.label}: \`${pair.value}\``).join('\n\n')}`).join('\n\n')
196
+ const headingLevel = await attribute(attrs.headingLevel) ?? 3
197
+ if (headingLevel !== 3 && headingLevel !== 4) throw new Error('DocumentValueList requires headingLevel 3 or 4')
198
+ return values.map(value => `${'#'.repeat(headingLevel)} ${value.title} {#${value.id}}\n\n${value.identifier ? `\`${value.identifier}\`\n\n` : ''}${value.values.map(pair => `${pair.label}: \`${pair.value}\``).join('\n\n')}`).join('\n\n')
199
+ }
200
+ if (name === 'DocumentCodeTable') {
201
+ const rows = await expression(attrs.rows.value) as { syntax: string, css: string }[]
202
+ if (!Array.isArray(rows) || rows.some(row => typeof row.syntax !== 'string' || typeof row.css !== 'string')) throw new Error('DocumentCodeTable requires literal syntax and CSS rows')
203
+ const code = (value: string) => `\`${value.replaceAll('|', '\\|')}\``
204
+ return `| ${attrs.label ?? t('Syntax')} | CSS |\n| --- | --- |\n${rows.map(row => `| ${code(row.syntax)} | ${code(row.css)} |`).join('\n')}`
173
205
  }
174
206
  if (name === 'DocumentFlow') {
175
207
  const steps = await expression(attrs.steps.value) as { title: string, description: string }[]
176
208
  return `**${attrs.title}**\n\n${steps.map((step, index) => `${index + 1}. **${step.title}** — ${step.description}`).join('\n')}${attrs.caption ? `\n\n${attrs.caption}` : ''}`
177
209
  }
178
- if (name === 'DocumentFileTree') {
210
+ if (name === 'DocumentFileTree' || name === 'ExplorerView') {
179
211
  type Entry = { name: string, description?: string, children?: Entry[] }
180
- const entries = await expression(attrs.entries.value) as Entry[]
212
+ const entries = await expression(attrs[name === 'ExplorerView' ? 'items' : 'entries'].value) as Entry[]
181
213
  const lines = (items: Entry[], level = 0): string => items.map(item => `${' '.repeat(level)}- \`${item.name}${item.children ? '/' : ''}\`${item.description ? ` — ${item.description}` : ''}${item.children?.length ? `\n${lines(item.children, level + 1)}` : ''}`).join('\n')
182
- return `**${attrs.title}**\n\n${lines(entries)}${attrs.caption ? `\n\n${attrs.caption}` : ''}`
214
+ return `${attrs.title ? `**${attrs.title}**\n\n` : ''}${lines(entries)}${attrs.caption ? `\n\n${attrs.caption}` : ''}`
183
215
  }
184
216
  if (name === 'Overview' && options.overview !== 'include') return syntaxMarkdown(rows)
185
217
  if (name === 'StylesheetExample') {
@@ -187,7 +219,7 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
187
219
  if (typeof attrs.title !== 'string' || typeof source !== 'string') throw new Error('StylesheetExample requires a literal title and source')
188
220
  return stylesheetExampleMarkdown(attrs.title, source)
189
221
  }
190
- if (name === 'FoundationTokens') return selectedSpecimensMarkdown(attrs.namespace, await expression(attrs.keys.value) as string[])
222
+ if (name === 'FoundationTokens') return selectedSpecimensMarkdown(attrs.namespace, await expression(attrs.keys.value) as string[], t)
191
223
  if (name === 'DemoPageViewport') return `[Open the resizable ${attrs.title} example](${attrs.src}). The viewport slider also supports arrow keys.`
192
224
  if (name === 'DemoConfiguredExample') {
193
225
  const configuration = typeof attrs.source === 'string' ? attrs.source : await expression(attrs.source.value) as string
@@ -246,9 +278,20 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
246
278
  if (/^[A-Z]/.test(name)) {
247
279
  const imported = imports[name]
248
280
  const siteRoot = file.slice(0, file.lastIndexOf('/content/'))
249
- const target = imported?.startsWith('~/site/') ? path.join(siteRoot, imported.slice(7))
281
+ let target = imported?.startsWith('~/site/') ? path.join(siteRoot, imported.slice(7))
250
282
  : imported?.startsWith('.') ? path.resolve(path.dirname(file), imported)
251
283
  : path.join(path.dirname(file), 'components', `${name}.mdx`)
284
+ target = target.replace(/\.(tw|ja|ko|cn)\.mdx$/, '.mdx')
285
+ if (target.endsWith('.mdx') && locale !== 'en') {
286
+ const original = await readFile(target, 'utf8').catch(() => '')
287
+ const metadata = parseFrontmatter(original).frontmatter
288
+ if (metadata.draft === true || metadata.disabled === true) throw new Error(`Unpublished source MDX document: ${target}`)
289
+ if (original.trim() && !isSharedMdxSample(target, original)) {
290
+ const translated = target.replace(/\.mdx$/, `.${locale}.mdx`)
291
+ if (await access(translated).then(() => true, () => false)) target = translated
292
+ else throw new Error(`Missing translated MDX include: ${translated}`)
293
+ }
294
+ }
252
295
  if (target.endsWith('.mdx') && await access(target).then(() => true, () => false)) {
253
296
  const result = await extractReferenceMdx(target, rows, [...stack, file], options)
254
297
  examples.push(...result.examples)
@@ -268,7 +311,14 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
268
311
  const end = child.position.end.offset - node.position.start.offset
269
312
  text = text.slice(0, start) + await render(child) + text.slice(end)
270
313
  }
271
- return node.type === 'heading' ? text.replace(/\\([{}])/g, '$1') : text
314
+ if (node.type === 'heading') {
315
+ text = text.replace(/\\([{}])/g, '$1')
316
+ if (node.depth >= 2 && node.depth <= 6) {
317
+ const id = headingIds.get(node)
318
+ if (!/\s+\{#[\w-]+\}\s*$/.test(text)) text = text.replace(/\s+#+\s*$/, '') + ` {#${id}}`
319
+ }
320
+ }
321
+ return text
272
322
  }
273
323
  const parts: string[] = []
274
324
  for (const child of tree.children) parts.push(await render(child))
@@ -30,8 +30,13 @@
30
30
  </script>
31
31
 
32
32
  <script lang="ts">
33
+ import '@master/internal-site/styles/system/stage.css'
34
+ import '@master/internal-site/styles/system/tray.css'
35
+ import '@master/internal-site/styles/system/tray-code.css'
36
+
33
37
  import '@master/internal-site/styles/demo.css'
34
38
  import { styleText } from './style'
39
+ import { getSite } from '@master/internal-site/site'
35
40
 
36
41
  /** One example is one tray: head, stage, source, caption. class and style belong to the stage; the chrome never joins the demonstrated layout. */
37
42
  let {
@@ -39,17 +44,18 @@
39
44
  frameClassName, class: className, style, children, ...props
40
45
  }: DemoProps = $props()
41
46
  const head = $derived(Boolean(title || description || controls))
47
+ const { t } = getSite()
42
48
  </script>
43
49
 
44
- {#snippet node(value: DemoNode)}{#if typeof value === 'function'}{@render value()}{:else}{value}{/if}{/snippet}
50
+ {#snippet node(value: DemoNode, translate = false)}{#if typeof value === 'function'}{@render value()}{:else}{translate ? t(value) : value}{/if}{/snippet}
45
51
 
46
52
  <figure {...props} class={['ui-tray', frameClassName]}>{#if head}<figcaption class="ui-tray-head">
47
53
  {#if title || description}<div>
48
- {#if title}<p class="ui-tray-title">{title}</p>{/if}
49
- {#if description}<p class="ui-tray-description">{@render node(description)}</p>{/if}
54
+ {#if title}<p class="ui-tray-title">{t(title)}</p>{/if}
55
+ {#if description}<p class="ui-tray-description">{@render node(description, true)}</p>{/if}
50
56
  </div>{/if}
51
57
  {#if controls}<div class="ui-tray-tools">{@render node(controls)}</div>{/if}
52
58
  </figcaption>{/if}{#if frame === 'none'}{@render children?.()}{:else}<div class={['ui-stage', className]} style={styleText(style)}
53
59
  data-background={background === 'plain' ? undefined : background}
54
60
  data-padding={padding === 'lg' ? undefined : padding}
55
- data-align={align === 'center' ? undefined : align}>{@render children?.()}</div>{/if}{#if source}{@render node(source)}{/if}{#if caption}{#if head}<div class="ui-tray-caption">{@render node(caption)}</div>{:else}<figcaption class="ui-tray-caption">{@render node(caption)}</figcaption>{/if}{/if}</figure>
61
+ data-align={align === 'center' ? undefined : align}>{@render children?.()}</div>{/if}{#if source}{@render node(source)}{/if}{#if caption}{#if head}<div class="ui-tray-caption">{@render node(caption, true)}</div>{:else}<figcaption class="ui-tray-caption">{@render node(caption, true)}</figcaption>{/if}{/if}</figure>
@@ -1,8 +1,10 @@
1
1
  ---
2
+ import '@master/internal-site/styles/interface.css'
2
3
  import '@master/internal-site/styles/system/specimens.css'
3
4
  import IconDownload from '@tabler/icons-svelte-runes/icons/download'
4
5
  import Demo from './Demo.svelte'
5
6
  import Image from '@master/internal-site/components/Image.svelte'
7
+ import { siteOf } from '../astro/site-context'
6
8
 
7
9
  export interface DemoAssetProps {
8
10
  title: string
@@ -22,17 +24,18 @@ type Props = DemoAssetProps
22
24
  * transparent artwork sits on a checkerboard. A demo container keeps the prose media perimeter off the artwork.
23
25
  */
24
26
  const { title, description, src, alt, width, height, format = 'SVG', surface = 'transparent' } = Astro.props
27
+ const { t } = siteOf(Astro)
25
28
  ---
26
29
 
27
30
  <Demo title={title} description={description} background={surface === 'transparent' ? 'checkerboard' : 'plain'} class={surface === 'transparent' ? undefined : surface} data-asset={surface}>
28
31
  <Fragment slot="caption">
29
32
  <span class="flex flex-wrap items-center justify-between gap-sm">
30
33
  <span class="ui-badge" data-size="sm">{format}</span>
31
- <a href={src} download class="ui-button" data-size="sm" aria-label={`Download ${title} (${format})`}>
34
+ <a href={src} download class="ui-button" data-size="sm" aria-label={t('Download {title} ({format})').replace('{title}', t(title)).replace('{format}', format)}>
32
35
  <IconDownload aria-hidden="true" />
33
- Download {format}
36
+ {t('Download {format}').replace('{format}', format)}
34
37
  </a>
35
38
  </span>
36
39
  </Fragment>
37
- <div class="grid (place-items:center) w-full" data-demo-container=""><Image src={src} alt={alt} width={width} height={height} class="block h-auto w-auto max-w-full (max-height:var(--spacing-4xl))" /></div>
40
+ <div class="grid (place-items:center) w-full" data-demo-container=""><Image src={src} alt={t(alt)} width={width} height={height} class="block h-auto w-auto max-w-full (max-height:var(--spacing-4xl))" /></div>
38
41
  </Demo>
@@ -1,11 +1,13 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
2
4
  import '@master/internal-site/styles/system/specimens.css'
3
5
  import type { Snippet } from 'svelte'
4
6
  import type { HTMLAttributes } from 'svelte/elements'
5
7
  import { styleText, type DemoStyle } from '@master/internal-site/demo/style.ts'
6
8
 
7
9
  /** `inline` and `block` take text, or a snippet (a named slot from `.astro` and `.mdx`). */
8
- let { inline = 'Main axis', block = 'Cross axis', children, class: className, style, ...props }: Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {
10
+ let { inline = t('Main axis'), block = t('Cross axis'), children, class: className, style, ...props }: Omit<HTMLAttributes<HTMLDivElement>, 'style'> & {
9
11
  inline?: string | Snippet
10
12
  block?: string | Snippet
11
13
  style?: DemoStyle
@@ -13,7 +15,7 @@
13
15
  } = $props()
14
16
  </script>
15
17
 
16
- {#snippet node(value: string | Snippet)}{#if typeof value === 'function'}{@render value()}{:else}{value}{/if}{/snippet}
18
+ {#snippet node(value: string | Snippet)}{#if typeof value === 'function'}{@render value()}{:else}{t(value)}{/if}{/snippet}
17
19
 
18
20
  <div {...props} class={['ui-axes', className]} style={styleText(style)}>
19
21
  <div class="ui-axes-inline"><span>{@render node(inline)}</span><span class="ui-axes-rule" aria-hidden="true"></span></div>
@@ -1,4 +1,7 @@
1
1
  ---
2
+ import { siteOf } from '@master/internal-site/astro/site-context.ts'
3
+ const { t } = siteOf(Astro)
4
+ import '@master/internal-site/styles/system/disclosure.css'
2
5
  import '@master/internal-site/styles/demo.css'
3
6
  import RenderNode from '@master/internal-site/components/RenderNode.astro'
4
7
  import DemoIndex from '@master/internal-site/demo/DemoIndex.astro'
@@ -19,16 +22,16 @@ const { groups } = Astro.props
19
22
  ---
20
23
 
21
24
  <div class="ui-catalog">
22
- <DemoIndex label="Utility recipe categories" groups={[{
23
- title: 'Choose a behavior',
24
- links: groups.map(group => ({ href: `#${group.id}`, label: `${group.title} · ${group.entries.length}` }))
25
+ <DemoIndex label={t('Utility recipe categories')} groups={[{
26
+ title: t('Choose a behavior'),
27
+ links: groups.map(group => ({ href: `#${group.id}`, label: `${t(group.title)} · ${group.entries.length}` }))
25
28
  }]} />
26
29
  {groups.map(group => <section aria-labelledby={group.id}>
27
- <h3 id={group.id}>{group.title}<small>{group.entries.length} recipes</small></h3>
30
+ <h3 id={group.id}>{t(group.title)}<small>{t('{count} recipes').replace('{count}', String(group.entries.length))}</small></h3>
28
31
  {group.entries.map(entry => <details class="ui-disclosure" data-variant="rule">
29
- <summary>{entry.title}<span class="ui-disclosure-meta">{entry.label}</span></summary>
32
+ <summary>{t(entry.title)}<span class="ui-disclosure-meta">{t(entry.label)}</span></summary>
30
33
  <div class="ui-catalog-entry">
31
- {entry.href && <a href={entry.href}>Read related documentation ↗</a>}
34
+ {entry.href && <a href={entry.href}>{t('Read related documentation')} ↗</a>}
32
35
  <RenderNode node={entry.children} />
33
36
  </div>
34
37
  </details>)}
@@ -10,6 +10,9 @@
10
10
  </script>
11
11
 
12
12
  <script lang="ts">
13
+ import '@master/internal-site/styles/system/compare.css'
14
+ import '@master/internal-site/styles/system/stage.css'
15
+
13
16
  import '@master/internal-site/styles/system/specimens.css'
14
17
  import type { HTMLAttributes } from 'svelte/elements'
15
18
  import { styleText, type DemoStyle } from '@master/internal-site/demo/style.ts'
@@ -1,4 +1,9 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
4
+ import '@master/internal-site/styles/interface.css'
5
+ import '@master/internal-site/styles/system/range.css'
6
+
2
7
  import '@master/internal-site/styles/system/specimens.css'
3
8
  import type { Snippet } from 'svelte'
4
9
  import type { HTMLAttributes } from 'svelte/elements'
@@ -35,12 +40,12 @@
35
40
  </script>
36
41
 
37
42
  {#snippet controls()}
38
- <label class="ui-label" data-case="as-is" for={id}>Container</label>
43
+ <label class="ui-label" data-case="as-is" for={id}>{t('Container')}</label>
39
44
  <input {id} class="ui-range" type="range" min={minimumWidth} max={maxWidth} step="1" disabled={!ready} {value}
40
45
  style:--value={`${((value - minimumWidth) / range) * 100}%`} style:width="128px" style:max-width="100%"
41
46
  oninput={event => { width = Number(event.currentTarget.value) }} />
42
- <button type="button" class="ui-button" data-size="sm" disabled={!ready} onclick={() => { width = undefined }}>Fit</button>
43
- <output class="ui-label" data-case="as-is">{ready ? `${measured} px · viewport unchanged` : 'Measuring container…'}</output>
47
+ <button type="button" class="ui-button" data-size="sm" disabled={!ready} onclick={() => { width = undefined }}>{t('Fit')}</button>
48
+ <output class="ui-label" data-case="as-is">{ready ? t('{width} px · viewport unchanged').replace('{width}', String(measured)) : t('Measuring container…')}</output>
44
49
  {/snippet}
45
50
 
46
51
  <Demo {...props} {title} {caption} padding="sm" data-ready={ready} {controls}>
@@ -1,4 +1,8 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
4
+ import '@master/internal-site/styles/system/segmented.css'
5
+
2
6
  import '@master/internal-site/styles/system/specimens.css'
3
7
  import type { Snippet } from 'svelte'
4
8
  import type { HTMLAttributes } from 'svelte/elements'
@@ -13,4 +17,4 @@
13
17
  } = $props()
14
18
  </script>
15
19
 
16
- <div {...props} role="group" aria-label={label} class={[variant === 'segmented' ? 'ui-segmented' : 'flex flex-wrap items-center gap-xs', className]} style={styleText(style)}>{@render children?.()}</div>
20
+ <div {...props} role="group" aria-label={t(label)} class={[variant === 'segmented' ? 'ui-segmented' : 'flex flex-wrap items-center gap-xs', className]} style={styleText(style)}>{@render children?.()}</div>
@@ -1,4 +1,9 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
4
+ import '@master/internal-site/styles/interface.css'
5
+ import '@master/internal-site/styles/system/copy.css'
6
+
2
7
  import type { Snippet } from 'svelte'
3
8
  import type { HTMLButtonAttributes } from 'svelte/elements'
4
9
  import IconCopy from '@tabler/icons-svelte-runes/icons/copy'
@@ -25,19 +30,19 @@
25
30
  async function copy() {
26
31
  if (busy) return
27
32
  busy = true
28
- report('Copying…', undefined)
33
+ report(t('Copying…'), undefined)
29
34
  try {
30
35
  if (!window.isSecureContext || !navigator.clipboard?.writeText) throw new Error('Clipboard unavailable')
31
36
  await navigator.clipboard.writeText(value)
32
- report(feedback === 'compact' ? 'Copied' : `Copied ${value}`, 'success')
37
+ report(feedback === 'compact' ? t('Copied') : t('Copied {value}').replace('{value}', value), 'success')
33
38
  } catch {
34
- report(feedback === 'compact' ? 'Copy unavailable' : `Copy unavailable: ${value}`, 'error')
39
+ report(feedback === 'compact' ? t('Copy unavailable') : t('Copy unavailable: {value}').replace('{value}', value), 'error')
35
40
  } finally { busy = false }
36
41
  }
37
42
  </script>
38
43
 
39
44
  {#snippet button()}
40
- <button data-size={className ? undefined : 'sm'} {...props} type="button" aria-label={label} class={className ?? 'ui-button'} style={styleText(style)} aria-busy={busy} onclick={copy}>
45
+ <button data-size={className ? undefined : 'sm'} {...props} type="button" aria-label={t(label)} class={className ?? 'ui-button'} style={styleText(style)} aria-busy={busy} onclick={copy}>
41
46
  {#if icon === undefined}<IconCopy aria-hidden="true" />{:else if icon}{@render icon()}{/if}{@render children?.()}
42
47
  </button>
43
48
  {/snippet}
@@ -1,15 +1,19 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
4
+ import '@master/internal-site/styles/system/copy.css'
5
+
2
6
  import type { Snippet } from 'svelte'
3
7
  import { setCopyFeedback, type CopyResult } from '@master/internal-site/demo/copy-feedback.ts'
4
8
 
5
9
  /** One status for a set of copy actions, such as a palette. The buttons must render in this group's Svelte tree. */
6
- let { children, instruction = 'Select a swatch to copy its variable.' }: { children?: Snippet, instruction?: string } = $props()
10
+ let { children, instruction = t('Select a swatch to copy its variable.') }: { children?: Snippet, instruction?: string } = $props()
7
11
  let state = $state<{ instruction: string, message: string, result: CopyResult }>()
8
12
  const current = $derived(state?.instruction === instruction ? state : undefined)
9
13
  setCopyFeedback((message, result) => { state = { instruction, message, result } })
10
14
  </script>
11
15
 
12
16
  <div class="grid gap-sm">
13
- <p class="m(0) ui-copy"><span class="ui-copy-status" role="status" data-result={current?.result}>{current?.message ?? instruction}</span></p>
17
+ <p class="m(0) ui-copy"><span class="ui-copy-status" role="status" data-result={current?.result}>{current?.message ?? t(instruction)}</span></p>
14
18
  {@render children?.()}
15
19
  </div>
@@ -1,4 +1,6 @@
1
1
  ---
2
+ import { siteOf } from '@master/internal-site/astro/site-context.ts'
3
+ const { t } = siteOf(Astro)
2
4
  import Demo from '@master/internal-site/demo/Demo.svelte'
3
5
  import DemoViewport from '@master/internal-site/demo/DemoViewport.svelte'
4
6
  import { referenceDemoSections } from '@master/internal-site/demo/reference/source.ts'
@@ -22,6 +24,6 @@ const printable = source.classes.some(value => value.includes('@media(print)'))
22
24
  ---
23
25
 
24
26
  <Demo title={source.title} frame="none" caption={scene.caption} data-demo-case={`${page}#${section}`}>
25
- <DemoViewport client:idle={{ timeout: 500 }} title={`${page}: ${source.title}`} document={demoDocument(source, scene)} responsive={scene.responsive ?? responsive}
27
+ <DemoViewport client:idle={{ timeout: 500 }} title={`${page}: ${t(source.title)}`} document={demoDocument(source, scene)} responsive={scene.responsive ?? responsive}
26
28
  theme={scene.theme ?? themed} print={printable} motion={scene.motion} inspect={scene.inspect ?? (section === 'apply-conditionally' ? [page] : undefined)} height={scene.height} maxWidth={scene.maxWidth} sizing={scene.sizing} />
27
29
  </Demo>
@@ -1,5 +1,9 @@
1
1
  <script lang="ts">
2
+ import '@master/internal-site/styles/system/inline-code.css'
3
+
2
4
  import '@master/internal-site/styles/system/specimens.css'
5
+ import { getSite } from '@master/internal-site/site'
6
+ const { t } = getSite()
3
7
  import { onMount } from 'svelte'
4
8
 
5
9
  /** Reports syntax support only; the example still owns its native CSS behavior. */
@@ -13,6 +17,6 @@
13
17
  <p class="flex flex-wrap items-center gap-xs m(0)" data-supported={supported ?? 'unknown'}>
14
18
  <code class="ui-inline-code">{condition}</code>
15
19
  <span class="ui-badge" data-variant="dot" data-tone={supported ? 'green' : undefined}>
16
- {supported === null ? 'Checking…' : supported ? 'Supported' : 'Not supported'}
20
+ {supported === null ? t('Checking…') : supported ? t('Supported') : t('Not supported')}
17
21
  </span>
18
22
  </p>
@@ -1,8 +1,12 @@
1
1
  <script lang="ts">
2
+ import '@master/internal-site/styles/system/disclosure.css'
3
+
2
4
  import type { Snippet } from 'svelte'
5
+ import { getSite } from '@master/internal-site/site'
3
6
 
4
7
  /** The complete generated stylesheet stays one native, closed disclosure under the source. */
5
8
  let { children }: { children?: Snippet } = $props()
9
+ const { t } = getSite()
6
10
  </script>
7
11
 
8
- <details class="ui-disclosure"><summary>Generated CSS</summary>{@render children?.()}</details>
12
+ <details class="ui-disclosure"><summary>{t('Generated CSS')}</summary>{@render children?.()}</details>
@@ -1,4 +1,7 @@
1
1
  ---
2
+ import { siteOf } from '@master/internal-site/astro/site-context.ts'
3
+ const { t } = siteOf(Astro)
4
+ import '@master/internal-site/styles/interface.css'
2
5
  import '@master/internal-site/styles/system/specimens.css'
3
6
 
4
7
  export interface DemoIndexGroup {
@@ -15,9 +18,9 @@ interface Props {
15
18
  const { label, groups } = Astro.props
16
19
  ---
17
20
 
18
- <nav class="grid gap-md" aria-label={label}>
21
+ <nav class="grid gap-md" aria-label={t(label)}>
19
22
  {groups.map(group => <div class="grid gap-xs">
20
- <p class="m(0) ui-label">{group.title}</p>
21
- <ul class="flex flex-wrap gap-2xs m(0) p(0) (list-style:none)">{group.links.map(link => <li><a class="ui-button" data-size="sm" href={link.href}>{link.label}</a></li>)}</ul>
23
+ <p class="m(0) ui-label">{t(group.title)}</p>
24
+ <ul class="flex flex-wrap gap-2xs m(0) p(0) (list-style:none)">{group.links.map(link => <li><a class="ui-button" data-size="sm" href={link.href}>{t(link.label)}</a></li>)}</ul>
22
25
  </div>)}
23
26
  </nav>
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
2
4
  import '@master/internal-site/styles/system/specimens.css'
3
5
  import type { HTMLAttributes } from 'svelte/elements'
4
6
  import { styleText, type DemoStyle } from '@master/internal-site/demo/style.ts'
@@ -10,6 +12,6 @@
10
12
  } = $props()
11
13
  </script>
12
14
 
13
- <ul {...props} class={['ui-legend', className]} style={styleText(style)} aria-label="Legend">
15
+ <ul {...props} class={['ui-legend', className]} style={styleText(style)} aria-label={t('Legend')}>
14
16
  {#each items as { label, tone: value } (label)}<li><i data-tone={tone(value)} aria-hidden="true"></i>{label}</li>{/each}
15
17
  </ul>
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
2
4
  import '@master/internal-site/styles/system/specimens.css'
3
5
  import type { Snippet } from 'svelte'
4
6
 
@@ -20,8 +22,8 @@
20
22
 
21
23
  <div class="ui-measure">
22
24
  <div class="ui-measure-head">
23
- <span>{label}</span>
24
- <output aria-label={`${label} dimensions`}>{size ? `${size.width} × ${size.height} px` : 'Measuring…'}</output>
25
+ <span>{t(label)}</span>
26
+ <output aria-label={t('{label} dimensions').replace('{label}', t(label))}>{size ? `${size.width} × ${size.height} px` : t('Measuring…')}</output>
25
27
  </div>
26
28
  <div class="ui-measure-rule" aria-hidden="true"></div>
27
29
  <div bind:this={element}>{@render children?.()}</div>
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
2
4
  import '@master/internal-site/styles/system/specimens.css'
3
5
  import type { ClassValue } from 'svelte/elements'
4
6
  import type { SVGAttributes } from 'svelte/elements'
@@ -27,7 +29,7 @@
27
29
  {@const imageProps: SiteImageProps = { width: 320, height: 200, ...(props as Omit<ImageProps, 'src' | 'alt' | 'style'>), style: styleText(style), src, alt: alt! }}
28
30
  <Image {...imageProps} />
29
31
  {:else}
30
- <svg viewBox="0 0 320 200" role="img" aria-label="Sun above two mountain ridges" {...props as SVGAttributes<SVGSVGElement>}
32
+ <svg viewBox="0 0 320 200" role="img" aria-label={t('Sun above two mountain ridges')} {...props as SVGAttributes<SVGSVGElement>}
31
33
  style={[`color:var(--ui-tone-${value})`, styleText(style)].filter(Boolean).join(';')}>
32
34
  <rect width="320" height="200" fill="var(--ui-surface-sheet)" />
33
35
  <path d="M0 175V158l92-93 113 110H0Z" fill="currentColor" opacity=".1" />
@@ -1,4 +1,7 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
4
+ import '@master/internal-site/styles/interface.css'
2
5
  import type { Snippet } from 'svelte'
3
6
  import { onMount } from 'svelte'
4
7
  import IconPlayerPause from '@tabler/icons-svelte-runes/icons/player-pause'
@@ -31,11 +34,11 @@
31
34
 
32
35
  <div class="grid gap-md w-full">
33
36
  <div bind:this={element} data-playback={playing ? 'playing' : 'paused'}>{@render children?.()}</div>
34
- <div class="ui-player" role="group" aria-label="Animation controls" data-state={playing ? 'playing' : 'paused'}>
37
+ <div class="ui-player" role="group" aria-label={t('Animation controls')} data-state={playing ? 'playing' : 'paused'}>
35
38
  <button type="button" class="ui-button" data-size="sm" onclick={() => control(playing ? 'pause' : 'play')}>
36
- {#if playing}<IconPlayerPause aria-hidden="true" />{:else}<IconPlayerPlay aria-hidden="true" />{/if}{playing ? 'Pause' : 'Play'}
39
+ {#if playing}<IconPlayerPause aria-hidden="true" />{:else}<IconPlayerPlay aria-hidden="true" />{/if}{playing ? t('Pause') : t('Play')}
37
40
  </button>
38
- <button type="button" class="ui-button" data-size="sm" data-variant="quiet" onclick={() => control('restart')}><IconRefresh aria-hidden="true" />Replay</button>
41
+ <button type="button" class="ui-button" data-size="sm" data-variant="quiet" onclick={() => control('restart')}><IconRefresh aria-hidden="true" />{t('Replay')}</button>
39
42
  <span class="ui-player-time"><output>{playing ? 'Playing' : 'Paused'}</output></span>
40
43
  </div>
41
44
  </div>
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import { getSite } from '@master/internal-site/site'
3
+ const { t } = getSite()
2
4
  import '@master/internal-site/styles/system/specimens.css'
3
5
  import DemoCopyButton from '@master/internal-site/demo/DemoCopyButton.svelte'
4
6
  import DemoCopyGroup from '@master/internal-site/demo/DemoCopyGroup.svelte'
@@ -8,8 +10,8 @@
8
10
  </script>
9
11
 
10
12
  <div class="w-full"><DemoCopyGroup><div class="ui-palette">
11
- {#each groups as { family, colors } (family)}<div class="ui-palette-row" role="group" aria-label={`${family} palette`}>
13
+ {#each groups as { family, colors } (family)}<div class="ui-palette-row" role="group" aria-label={t('{family} palette').replace('{family}', family)}>
12
14
  <span>{family}</span>
13
- {#each colors as { name, value } (name)}<DemoCopyButton value={`var(--${name})`} label={`Copy ${name}`} icon={null} class="ui-palette-chip" style={`background-color:${value}`} title={`${name}: ${value}`} />{/each}
15
+ {#each colors as { name, value } (name)}<DemoCopyButton value={`var(--${name})`} label={t('Copy {name}').replace('{name}', name)} icon={null} class="ui-palette-chip" style={`background-color:${value}`} title={`${name}: ${value}`} />{/each}
14
16
  </div>{/each}
15
17
  </div></DemoCopyGroup></div>
@@ -1,4 +1,6 @@
1
1
  <script lang="ts">
2
+ import '@master/internal-site/styles/system/scroll.css'
3
+
2
4
  import '@master/internal-site/styles/system/specimens.css'
3
5
  import type { Snippet } from 'svelte'
4
6
  import type { HTMLAttributes } from 'svelte/elements'
@@ -1,4 +1,7 @@
1
1
  ---
2
+ import { siteOf } from '@master/internal-site/astro/site-context'
3
+ const { t } = siteOf(Astro)
4
+ import '@master/internal-site/styles/system/compare.css'
2
5
  import Demo from '@master/internal-site/demo/Demo.svelte'
3
6
  import DemoViewport from '@master/internal-site/demo/DemoViewport.svelte'
4
7
 
@@ -17,9 +20,9 @@ const { html, css, height = 320, sizing = 'viewport', caption } = Astro.props
17
20
  const document = (styles: string) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><style>${styles.replaceAll('</style', '<\\/style')}</style></head><body>${html}</body></html>`
18
21
  ---
19
22
 
20
- <Demo title="Browser defaults and author CSS" description="The same HTML in two documents" frame="none" caption={caption}>
23
+ <Demo title={t("Browser defaults and author CSS")} description={t("The same HTML in two documents")} frame="none" caption={caption}>
21
24
  <div class="ui-compare">
22
- <figure><figcaption>Browser defaults</figcaption><DemoViewport client:idle={{ timeout: 500 }} title="Browser defaults" document={document('')} height={height} sizing={sizing} /></figure>
23
- <figure><figcaption>CSS applied</figcaption><DemoViewport client:idle={{ timeout: 500 }} title="CSS applied" document={document(css)} height={height} sizing={sizing} /></figure>
25
+ <figure><figcaption>{t("Browser defaults")}</figcaption><DemoViewport client:idle={{ timeout: 500 }} title={t("Browser defaults")} document={document('')} height={height} sizing={sizing} /></figure>
26
+ <figure><figcaption>{t("CSS applied")}</figcaption><DemoViewport client:idle={{ timeout: 500 }} title={t("CSS applied")} document={document(css)} height={height} sizing={sizing} /></figure>
24
27
  </div>
25
28
  </Demo>