@master/internal-site 0.1.3 → 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.
- package/README.md +37 -0
- package/dist/astro.mjs +92 -41
- package/dist/config.d.mts +6 -0
- package/dist/localization-LfIbIysE.mjs +356 -0
- package/dist/localization.d.mts +69 -0
- package/dist/localization.mjs +1 -0
- package/package.json +12 -9
- package/src/astro/content-pages.ts +30 -11
- package/src/astro/index.ts +9 -5
- package/src/astro/localized-mdx.ts +45 -0
- package/src/astro/routes.template.ts +3 -0
- package/src/astro/site-context.ts +10 -2
- package/src/benchmarks/BenchmarkChart.astro +4 -1
- package/src/benchmarks/BenchmarkMetricTable.astro +1 -1
- package/src/benchmarks/BenchmarkStackedBars.astro +2 -2
- package/src/benchmarks/BenchmarkStatus.astro +3 -1
- package/src/benchmarks/localization.ts +22 -0
- package/src/blog/article/BlogArticleEnd.astro +6 -9
- package/src/blog/article/BlogArticleMasthead.astro +5 -8
- package/src/config.ts +3 -0
- package/src/css/foundation-data/specimen-content.ts +2 -2
- package/src/css/markdown.ts +52 -11
- package/src/demo/Demo.svelte +6 -4
- package/src/demo/DemoAsset.astro +5 -3
- package/src/demo/DemoAxes.svelte +4 -2
- package/src/demo/DemoCatalog.astro +8 -6
- package/src/demo/DemoContainer.svelte +5 -3
- package/src/demo/DemoControls.svelte +3 -1
- package/src/demo/DemoCopyButton.svelte +6 -4
- package/src/demo/DemoCopyGroup.svelte +4 -2
- package/src/demo/DemoExample.astro +3 -1
- package/src/demo/DemoFeatureSupport.svelte +3 -1
- package/src/demo/DemoGeneratedCSS.svelte +3 -1
- package/src/demo/DemoIndex.astro +5 -3
- package/src/demo/DemoLegend.svelte +3 -1
- package/src/demo/DemoMeasure.svelte +4 -2
- package/src/demo/DemoMedia.svelte +3 -1
- package/src/demo/DemoMotion.svelte +5 -3
- package/src/demo/DemoPaletteGroups.svelte +4 -2
- package/src/demo/DemoStyleComparison.astro +5 -3
- package/src/demo/DemoThemeComparison.astro +4 -2
- package/src/demo/DemoTokenTable.astro +4 -2
- package/src/demo/DemoViewTransition.astro +3 -1
- package/src/demo/DemoViewport.svelte +13 -11
- package/src/demo/DemoWaterfall.astro +3 -1
- package/src/demo/ProjectStyleExample.astro +4 -1
- package/src/demo/foundations/FoundationDialog.astro +5 -2
- package/src/demo/foundations/FoundationTokensView.astro +16 -5
- package/src/demo/foundations/Palette.svelte +9 -7
- package/src/demo/foundations/dialog-copy.d.ts +1 -0
- package/src/demo/foundations/dialog-copy.js +5 -0
- package/src/docs-shell/common/i18n.config.d.ts +1 -1
- package/src/docs-shell/common/i18n.config.js +4 -12
- package/src/docs-shell/common/locales.d.ts +8 -0
- package/src/docs-shell/common/locales.js +18 -0
- package/src/docs-shell/common/terminology.json +32 -0
- package/src/docs-shell/common/translations.d.ts +3 -0
- package/src/docs-shell/common/translations.js +294 -0
- package/src/docs-shell/components/ArticleHeader.astro +3 -3
- package/src/docs-shell/components/CodeCopyButton.astro +3 -1
- package/src/docs-shell/components/CodeTabBar.astro +5 -3
- package/src/docs-shell/components/DocHeader.svelte +3 -4
- package/src/docs-shell/components/DocHeading.astro +3 -1
- package/src/docs-shell/components/DocMenuButton.svelte +4 -5
- package/src/docs-shell/components/DocSidebar.svelte +1 -2
- package/src/docs-shell/components/DocumentCodeTable.astro +3 -1
- package/src/docs-shell/components/DocumentIllustration.astro +3 -1
- package/src/docs-shell/components/DocumentKeyList.astro +5 -3
- package/src/docs-shell/components/DocumentNamespaceTable.astro +4 -2
- package/src/docs-shell/components/DocumentOptions.astro +3 -1
- package/src/docs-shell/components/DocumentationIndex.svelte +6 -7
- package/src/docs-shell/components/GeneratedCSS.astro +3 -1
- package/src/docs-shell/components/Link.svelte +3 -2
- package/src/docs-shell/components/NotFound.astro +3 -4
- package/src/docs-shell/components/PageNavs.astro +2 -3
- package/src/docs-shell/components/SearchDialog.svelte +13 -16
- package/src/docs-shell/components/TimeAgo.svelte +6 -6
- package/src/docs-shell/contexts/search.ts +1 -1
- package/src/docs-shell/factories/Page.astro +10 -5
- package/src/docs-shell/factories/create-page.ts +33 -6
- package/src/docs-shell/layouts/BlogArticle.astro +3 -11
- package/src/docs-shell/remark/slug-and-toc.js +5 -7
- package/src/docs-shell/scripts/code.ts +5 -2
- package/src/docs-shell/site.ts +9 -1
- package/src/docs-shell/utils/document-copy.d.ts +2 -0
- package/src/docs-shell/utils/document-copy.js +18 -0
- package/src/docs-shell/utils/generate-metadata.ts +2 -0
- package/src/docs-shell/utils/i18n-pathname.ts +13 -2
- package/src/docs-shell/utils/i18n.ts +4 -1
- package/src/docs-shell/utils/localized-metadata.ts +22 -0
- package/src/docs-shell/utils/markdown-links.ts +61 -0
- package/src/docs-shell/utils/markdown-tree.ts +19 -6
- package/src/docs-shell/utils/resolve-heading.d.ts +1 -0
- package/src/docs-shell/utils/resolve-heading.js +21 -0
- package/src/docs-shell/utils/search-pages.ts +75 -22
- package/src/home/hero.css +5 -0
- package/src/layouts/Document.astro +1 -1
- package/src/scripts/localization.ts +239 -0
- package/src/scripts/mdx-sample.ts +12 -0
- package/src/scripts/static-social.ts +17 -3
- package/src/scripts/style-delivery.mjs +14 -4
- package/src/sections/guide.astro +3 -1
- package/src/virtual.d.ts +5 -1
- package/src/blog/article/language.ts +0 -29
package/src/css/markdown.ts
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
|
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,11 +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 sourceLabel = attrs.sourceLabel ?? (result !== undefined && attrs.resultKind !== 'generated-css' ? 'Source' : undefined)
|
|
174
|
+
const sourceLabel = attrs.sourceLabel ?? (result !== undefined && attrs.resultKind !== 'generated-css' ? t('Source') : undefined)
|
|
152
175
|
const parts = [`**${attrs.title}**`, ...(sourceLabel ? [`**${sourceLabel}**`] : []), `\`\`\`${attrs.language}\n${source}\n\`\`\``]
|
|
153
176
|
if (result !== undefined) {
|
|
154
177
|
if (typeof result !== 'string') throw new Error('DocumentCodeExample requires a literal result')
|
|
155
|
-
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\`\`\``)
|
|
156
179
|
}
|
|
157
180
|
if (diagnostic) parts.push(`${diagnostic.severity} (\`${diagnostic.rule}\`): ${diagnostic.message}`)
|
|
158
181
|
return parts.join('\n\n')
|
|
@@ -160,7 +183,7 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
|
|
|
160
183
|
if (['DocumentOptions', 'DocumentParameters'].includes(name)) {
|
|
161
184
|
const values = await expression(attrs[name === 'DocumentOptions' ? 'options' : 'parameters'].value) as { name: string, type?: string, requirement?: string, defaultValue?: string, description: string }[]
|
|
162
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`)
|
|
163
|
-
return values.map(value => `**\`${value.name}\`**${value.type ? ` — \`${value.type}\`` : ''}${value.requirement ? ` (${value.requirement})` : ''}\n\n${value.defaultValue !== undefined ?
|
|
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')
|
|
164
187
|
}
|
|
165
188
|
if (name === 'DocumentAPIIndex') {
|
|
166
189
|
const values = await expression(attrs.entries.value) as { name: string, href: string, description: string }[]
|
|
@@ -178,7 +201,7 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
|
|
|
178
201
|
const rows = await expression(attrs.rows.value) as { syntax: string, css: string }[]
|
|
179
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')
|
|
180
203
|
const code = (value: string) => `\`${value.replaceAll('|', '\\|')}\``
|
|
181
|
-
return `| ${attrs.label ?? 'Syntax'} | CSS |\n| --- | --- |\n${rows.map(row => `| ${code(row.syntax)} | ${code(row.css)} |`).join('\n')}`
|
|
204
|
+
return `| ${attrs.label ?? t('Syntax')} | CSS |\n| --- | --- |\n${rows.map(row => `| ${code(row.syntax)} | ${code(row.css)} |`).join('\n')}`
|
|
182
205
|
}
|
|
183
206
|
if (name === 'DocumentFlow') {
|
|
184
207
|
const steps = await expression(attrs.steps.value) as { title: string, description: string }[]
|
|
@@ -196,7 +219,7 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
|
|
|
196
219
|
if (typeof attrs.title !== 'string' || typeof source !== 'string') throw new Error('StylesheetExample requires a literal title and source')
|
|
197
220
|
return stylesheetExampleMarkdown(attrs.title, source)
|
|
198
221
|
}
|
|
199
|
-
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)
|
|
200
223
|
if (name === 'DemoPageViewport') return `[Open the resizable ${attrs.title} example](${attrs.src}). The viewport slider also supports arrow keys.`
|
|
201
224
|
if (name === 'DemoConfiguredExample') {
|
|
202
225
|
const configuration = typeof attrs.source === 'string' ? attrs.source : await expression(attrs.source.value) as string
|
|
@@ -255,9 +278,20 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
|
|
|
255
278
|
if (/^[A-Z]/.test(name)) {
|
|
256
279
|
const imported = imports[name]
|
|
257
280
|
const siteRoot = file.slice(0, file.lastIndexOf('/content/'))
|
|
258
|
-
|
|
281
|
+
let target = imported?.startsWith('~/site/') ? path.join(siteRoot, imported.slice(7))
|
|
259
282
|
: imported?.startsWith('.') ? path.resolve(path.dirname(file), imported)
|
|
260
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
|
+
}
|
|
261
295
|
if (target.endsWith('.mdx') && await access(target).then(() => true, () => false)) {
|
|
262
296
|
const result = await extractReferenceMdx(target, rows, [...stack, file], options)
|
|
263
297
|
examples.push(...result.examples)
|
|
@@ -277,7 +311,14 @@ export async function extractReferenceMdx(file: string, rows: SyntaxRow[] = [],
|
|
|
277
311
|
const end = child.position.end.offset - node.position.start.offset
|
|
278
312
|
text = text.slice(0, start) + await render(child) + text.slice(end)
|
|
279
313
|
}
|
|
280
|
-
|
|
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
|
|
281
322
|
}
|
|
282
323
|
const parts: string[] = []
|
|
283
324
|
for (const child of tree.children) parts.push(await render(child))
|
package/src/demo/Demo.svelte
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
import '@master/internal-site/styles/demo.css'
|
|
38
38
|
import { styleText } from './style'
|
|
39
|
+
import { getSite } from '@master/internal-site/site'
|
|
39
40
|
|
|
40
41
|
/** One example is one tray: head, stage, source, caption. class and style belong to the stage; the chrome never joins the demonstrated layout. */
|
|
41
42
|
let {
|
|
@@ -43,17 +44,18 @@
|
|
|
43
44
|
frameClassName, class: className, style, children, ...props
|
|
44
45
|
}: DemoProps = $props()
|
|
45
46
|
const head = $derived(Boolean(title || description || controls))
|
|
47
|
+
const { t } = getSite()
|
|
46
48
|
</script>
|
|
47
49
|
|
|
48
|
-
{#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}
|
|
49
51
|
|
|
50
52
|
<figure {...props} class={['ui-tray', frameClassName]}>{#if head}<figcaption class="ui-tray-head">
|
|
51
53
|
{#if title || description}<div>
|
|
52
|
-
{#if title}<p class="ui-tray-title">{title}</p>{/if}
|
|
53
|
-
{#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}
|
|
54
56
|
</div>{/if}
|
|
55
57
|
{#if controls}<div class="ui-tray-tools">{@render node(controls)}</div>{/if}
|
|
56
58
|
</figcaption>{/if}{#if frame === 'none'}{@render children?.()}{:else}<div class={['ui-stage', className]} style={styleText(style)}
|
|
57
59
|
data-background={background === 'plain' ? undefined : background}
|
|
58
60
|
data-padding={padding === 'lg' ? undefined : padding}
|
|
59
|
-
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>
|
package/src/demo/DemoAsset.astro
CHANGED
|
@@ -4,6 +4,7 @@ import '@master/internal-site/styles/system/specimens.css'
|
|
|
4
4
|
import IconDownload from '@tabler/icons-svelte-runes/icons/download'
|
|
5
5
|
import Demo from './Demo.svelte'
|
|
6
6
|
import Image from '@master/internal-site/components/Image.svelte'
|
|
7
|
+
import { siteOf } from '../astro/site-context'
|
|
7
8
|
|
|
8
9
|
export interface DemoAssetProps {
|
|
9
10
|
title: string
|
|
@@ -23,17 +24,18 @@ type Props = DemoAssetProps
|
|
|
23
24
|
* transparent artwork sits on a checkerboard. A demo container keeps the prose media perimeter off the artwork.
|
|
24
25
|
*/
|
|
25
26
|
const { title, description, src, alt, width, height, format = 'SVG', surface = 'transparent' } = Astro.props
|
|
27
|
+
const { t } = siteOf(Astro)
|
|
26
28
|
---
|
|
27
29
|
|
|
28
30
|
<Demo title={title} description={description} background={surface === 'transparent' ? 'checkerboard' : 'plain'} class={surface === 'transparent' ? undefined : surface} data-asset={surface}>
|
|
29
31
|
<Fragment slot="caption">
|
|
30
32
|
<span class="flex flex-wrap items-center justify-between gap-sm">
|
|
31
33
|
<span class="ui-badge" data-size="sm">{format}</span>
|
|
32
|
-
<a href={src} download class="ui-button" data-size="sm" aria-label={
|
|
34
|
+
<a href={src} download class="ui-button" data-size="sm" aria-label={t('Download {title} ({format})').replace('{title}', t(title)).replace('{format}', format)}>
|
|
33
35
|
<IconDownload aria-hidden="true" />
|
|
34
|
-
Download {format}
|
|
36
|
+
{t('Download {format}').replace('{format}', format)}
|
|
35
37
|
</a>
|
|
36
38
|
</span>
|
|
37
39
|
</Fragment>
|
|
38
|
-
<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>
|
|
39
41
|
</Demo>
|
package/src/demo/DemoAxes.svelte
CHANGED
|
@@ -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,6 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { siteOf } from '@master/internal-site/astro/site-context.ts'
|
|
3
|
+
const { t } = siteOf(Astro)
|
|
2
4
|
import '@master/internal-site/styles/system/disclosure.css'
|
|
3
5
|
import '@master/internal-site/styles/demo.css'
|
|
4
6
|
import RenderNode from '@master/internal-site/components/RenderNode.astro'
|
|
@@ -20,16 +22,16 @@ const { groups } = Astro.props
|
|
|
20
22
|
---
|
|
21
23
|
|
|
22
24
|
<div class="ui-catalog">
|
|
23
|
-
<DemoIndex label=
|
|
24
|
-
title: 'Choose a behavior',
|
|
25
|
-
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}` }))
|
|
26
28
|
}]} />
|
|
27
29
|
{groups.map(group => <section aria-labelledby={group.id}>
|
|
28
|
-
<h3 id={group.id}>{group.title}<small>{group.entries.length}
|
|
30
|
+
<h3 id={group.id}>{t(group.title)}<small>{t('{count} recipes').replace('{count}', String(group.entries.length))}</small></h3>
|
|
29
31
|
{group.entries.map(entry => <details class="ui-disclosure" data-variant="rule">
|
|
30
|
-
<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>
|
|
31
33
|
<div class="ui-catalog-entry">
|
|
32
|
-
{entry.href && <a href={entry.href}>Read related documentation ↗</a>}
|
|
34
|
+
{entry.href && <a href={entry.href}>{t('Read related documentation')} ↗</a>}
|
|
33
35
|
<RenderNode node={entry.children} />
|
|
34
36
|
</div>
|
|
35
37
|
</details>)}
|
|
@@ -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/interface.css'
|
|
3
5
|
import '@master/internal-site/styles/system/range.css'
|
|
4
6
|
|
|
@@ -38,12 +40,12 @@
|
|
|
38
40
|
</script>
|
|
39
41
|
|
|
40
42
|
{#snippet controls()}
|
|
41
|
-
<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>
|
|
42
44
|
<input {id} class="ui-range" type="range" min={minimumWidth} max={maxWidth} step="1" disabled={!ready} {value}
|
|
43
45
|
style:--value={`${((value - minimumWidth) / range) * 100}%`} style:width="128px" style:max-width="100%"
|
|
44
46
|
oninput={event => { width = Number(event.currentTarget.value) }} />
|
|
45
|
-
<button type="button" class="ui-button" data-size="sm" disabled={!ready} onclick={() => { width = undefined }}>Fit</button>
|
|
46
|
-
<output class="ui-label" data-case="as-is">{ready ?
|
|
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>
|
|
47
49
|
{/snippet}
|
|
48
50
|
|
|
49
51
|
<Demo {...props} {title} {caption} padding="sm" data-ready={ready} {controls}>
|
|
@@ -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/segmented.css'
|
|
3
5
|
|
|
4
6
|
import '@master/internal-site/styles/system/specimens.css'
|
|
@@ -15,4 +17,4 @@
|
|
|
15
17
|
} = $props()
|
|
16
18
|
</script>
|
|
17
19
|
|
|
18
|
-
<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,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/interface.css'
|
|
3
5
|
import '@master/internal-site/styles/system/copy.css'
|
|
4
6
|
|
|
@@ -28,19 +30,19 @@
|
|
|
28
30
|
async function copy() {
|
|
29
31
|
if (busy) return
|
|
30
32
|
busy = true
|
|
31
|
-
report('Copying…', undefined)
|
|
33
|
+
report(t('Copying…'), undefined)
|
|
32
34
|
try {
|
|
33
35
|
if (!window.isSecureContext || !navigator.clipboard?.writeText) throw new Error('Clipboard unavailable')
|
|
34
36
|
await navigator.clipboard.writeText(value)
|
|
35
|
-
report(feedback === 'compact' ? 'Copied' :
|
|
37
|
+
report(feedback === 'compact' ? t('Copied') : t('Copied {value}').replace('{value}', value), 'success')
|
|
36
38
|
} catch {
|
|
37
|
-
report(feedback === 'compact' ? 'Copy unavailable' :
|
|
39
|
+
report(feedback === 'compact' ? t('Copy unavailable') : t('Copy unavailable: {value}').replace('{value}', value), 'error')
|
|
38
40
|
} finally { busy = false }
|
|
39
41
|
}
|
|
40
42
|
</script>
|
|
41
43
|
|
|
42
44
|
{#snippet button()}
|
|
43
|
-
<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}>
|
|
44
46
|
{#if icon === undefined}<IconCopy aria-hidden="true" />{:else if icon}{@render icon()}{/if}{@render children?.()}
|
|
45
47
|
</button>
|
|
46
48
|
{/snippet}
|
|
@@ -1,17 +1,19 @@
|
|
|
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/copy.css'
|
|
3
5
|
|
|
4
6
|
import type { Snippet } from 'svelte'
|
|
5
7
|
import { setCopyFeedback, type CopyResult } from '@master/internal-site/demo/copy-feedback.ts'
|
|
6
8
|
|
|
7
9
|
/** One status for a set of copy actions, such as a palette. The buttons must render in this group's Svelte tree. */
|
|
8
|
-
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()
|
|
9
11
|
let state = $state<{ instruction: string, message: string, result: CopyResult }>()
|
|
10
12
|
const current = $derived(state?.instruction === instruction ? state : undefined)
|
|
11
13
|
setCopyFeedback((message, result) => { state = { instruction, message, result } })
|
|
12
14
|
</script>
|
|
13
15
|
|
|
14
16
|
<div class="grid gap-sm">
|
|
15
|
-
<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>
|
|
16
18
|
{@render children?.()}
|
|
17
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>
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import '@master/internal-site/styles/system/inline-code.css'
|
|
3
3
|
|
|
4
4
|
import '@master/internal-site/styles/system/specimens.css'
|
|
5
|
+
import { getSite } from '@master/internal-site/site'
|
|
6
|
+
const { t } = getSite()
|
|
5
7
|
import { onMount } from 'svelte'
|
|
6
8
|
|
|
7
9
|
/** Reports syntax support only; the example still owns its native CSS behavior. */
|
|
@@ -15,6 +17,6 @@
|
|
|
15
17
|
<p class="flex flex-wrap items-center gap-xs m(0)" data-supported={supported ?? 'unknown'}>
|
|
16
18
|
<code class="ui-inline-code">{condition}</code>
|
|
17
19
|
<span class="ui-badge" data-variant="dot" data-tone={supported ? 'green' : undefined}>
|
|
18
|
-
{supported === null ? 'Checking…' : supported ? 'Supported' : 'Not supported'}
|
|
20
|
+
{supported === null ? t('Checking…') : supported ? t('Supported') : t('Not supported')}
|
|
19
21
|
</span>
|
|
20
22
|
</p>
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
import '@master/internal-site/styles/system/disclosure.css'
|
|
3
3
|
|
|
4
4
|
import type { Snippet } from 'svelte'
|
|
5
|
+
import { getSite } from '@master/internal-site/site'
|
|
5
6
|
|
|
6
7
|
/** The complete generated stylesheet stays one native, closed disclosure under the source. */
|
|
7
8
|
let { children }: { children?: Snippet } = $props()
|
|
9
|
+
const { t } = getSite()
|
|
8
10
|
</script>
|
|
9
11
|
|
|
10
|
-
<details class="ui-disclosure"><summary>Generated CSS</summary>{@render children?.()}</details>
|
|
12
|
+
<details class="ui-disclosure"><summary>{t('Generated CSS')}</summary>{@render children?.()}</details>
|
package/src/demo/DemoIndex.astro
CHANGED
|
@@ -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 '@master/internal-site/styles/interface.css'
|
|
3
5
|
import '@master/internal-site/styles/system/specimens.css'
|
|
4
6
|
|
|
@@ -16,9 +18,9 @@ interface Props {
|
|
|
16
18
|
const { label, groups } = Astro.props
|
|
17
19
|
---
|
|
18
20
|
|
|
19
|
-
<nav class="grid gap-md" aria-label={label}>
|
|
21
|
+
<nav class="grid gap-md" aria-label={t(label)}>
|
|
20
22
|
{groups.map(group => <div class="grid gap-xs">
|
|
21
|
-
<p class="m(0) ui-label">{group.title}</p>
|
|
22
|
-
<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>
|
|
23
25
|
</div>)}
|
|
24
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=
|
|
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={
|
|
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=
|
|
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,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/interface.css'
|
|
3
5
|
import type { Snippet } from 'svelte'
|
|
4
6
|
import { onMount } from 'svelte'
|
|
@@ -32,11 +34,11 @@
|
|
|
32
34
|
|
|
33
35
|
<div class="grid gap-md w-full">
|
|
34
36
|
<div bind:this={element} data-playback={playing ? 'playing' : 'paused'}>{@render children?.()}</div>
|
|
35
|
-
<div class="ui-player" role="group" aria-label=
|
|
37
|
+
<div class="ui-player" role="group" aria-label={t('Animation controls')} data-state={playing ? 'playing' : 'paused'}>
|
|
36
38
|
<button type="button" class="ui-button" data-size="sm" onclick={() => control(playing ? 'pause' : 'play')}>
|
|
37
|
-
{#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')}
|
|
38
40
|
</button>
|
|
39
|
-
<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>
|
|
40
42
|
<span class="ui-player-time"><output>{playing ? 'Playing' : 'Paused'}</output></span>
|
|
41
43
|
</div>
|
|
42
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={
|
|
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={
|
|
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
|
---
|
|
2
|
+
import { siteOf } from '@master/internal-site/astro/site-context'
|
|
3
|
+
const { t } = siteOf(Astro)
|
|
2
4
|
import '@master/internal-site/styles/system/compare.css'
|
|
3
5
|
import Demo from '@master/internal-site/demo/Demo.svelte'
|
|
4
6
|
import DemoViewport from '@master/internal-site/demo/DemoViewport.svelte'
|
|
@@ -18,9 +20,9 @@ const { html, css, height = 320, sizing = 'viewport', caption } = Astro.props
|
|
|
18
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>`
|
|
19
21
|
---
|
|
20
22
|
|
|
21
|
-
<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}>
|
|
22
24
|
<div class="ui-compare">
|
|
23
|
-
<figure><figcaption>Browser defaults</figcaption><DemoViewport client:idle={{ timeout: 500 }} title="Browser defaults" document={document('')} height={height} sizing={sizing} /></figure>
|
|
24
|
-
<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>
|
|
25
27
|
</div>
|
|
26
28
|
</Demo>
|
|
@@ -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 '@master/internal-site/styles/system/compare.css'
|
|
3
5
|
import Demo from '@master/internal-site/demo/Demo.svelte'
|
|
4
6
|
import DemoViewport from '@master/internal-site/demo/DemoViewport.svelte'
|
|
@@ -24,8 +26,8 @@ const document = (mode: 'light' | 'dark') => demoDocument({ page: 'foundations',
|
|
|
24
26
|
<Demo title={title} frame="none" caption={caption} data-foundation-paint={name}>
|
|
25
27
|
<div class="ui-compare">
|
|
26
28
|
{(['light', 'dark'] as const).map(mode => <figure data-theme-specimen={mode}>
|
|
27
|
-
<figcaption>{mode === 'light' ? 'Light' : 'Dark'}</figcaption>
|
|
28
|
-
<DemoViewport client:idle={{ timeout: 500 }} title={`${title} · ${mode}`} document={document(mode)} initialTheme={mode} print={print} height={height} sizing="content" />
|
|
29
|
+
<figcaption>{mode === 'light' ? t('Light') : t('Dark')}</figcaption>
|
|
30
|
+
<DemoViewport client:idle={{ timeout: 500 }} title={`${t(title)} · ${mode === 'light' ? t('Light') : t('Dark')}`} document={document(mode)} initialTheme={mode} print={print} height={height} sizing="content" />
|
|
29
31
|
</figure>)}
|
|
30
32
|
</div>
|
|
31
33
|
</Demo>
|
|
@@ -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 '@master/internal-site/styles/system/table.css'
|
|
3
5
|
import InlineCode from '@master/internal-site/components/InlineCode.astro'
|
|
4
6
|
import RenderNode from '@master/internal-site/components/RenderNode.astro'
|
|
@@ -22,8 +24,8 @@ const { rows, descriptionTitle = 'Role' } = Astro.props
|
|
|
22
24
|
---
|
|
23
25
|
|
|
24
26
|
{/* Narrow token inventories need native keyboard scrolling. */}
|
|
25
|
-
<div class="ui-table" role="region" aria-label={
|
|
26
|
-
<thead><tr><th scope="col">Token / class</th><th scope="col" class="(min-width:12rem)">{descriptionTitle}</th></tr></thead>
|
|
27
|
+
<div class="ui-table" role="region" aria-label={t('{label} token table').replace('{label}', t(descriptionTitle))} tabindex="0"><table>
|
|
28
|
+
<thead><tr><th scope="col">{t('Token / class')}</th><th scope="col" class="(min-width:12rem)">{t(descriptionTitle)}</th></tr></thead>
|
|
27
29
|
<tbody>{rows.map(({ token, utilities, value, description, preview }) => <tr>
|
|
28
30
|
<td>
|
|
29
31
|
<div class="whitespace-nowrap">{preview != null && <RenderNode node={preview} />}<code>{token}</code></div>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
+
import { siteOf } from '@master/internal-site/astro/site-context'
|
|
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
|
|
|
@@ -14,5 +16,5 @@ const title = articles ? 'From collection to article' : 'A change of view'
|
|
|
14
16
|
|
|
15
17
|
<Demo title={title} frame="none" data-view-transition={example}
|
|
16
18
|
caption={articles ? 'Open an article, then return to its card. Each image and title keeps a unique snapshot name. Keyboard focus follows the view change.' : 'Choose a view. The panel and heading use named snapshots; unsupported browsers and reduced motion use the same immediate update.'}>
|
|
17
|
-
<DemoViewport client:idle={{ timeout: 500 }} title={title} src={origin + (articles ? '/examples/article-transitions' : '/examples/view-transitions')} height={articles ? 640 : 340} sizing="viewport" />
|
|
19
|
+
<DemoViewport client:idle={{ timeout: 500 }} title={t(title)} src={origin + (articles ? '/examples/article-transitions' : '/examples/view-transitions')} height={articles ? 640 : 340} sizing="viewport" />
|
|
18
20
|
</Demo>
|