@sarj/docs-ui 0.2.0 → 0.4.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 +6 -2
- package/package.json +10 -4
- package/src/catalog.ts +28 -10
- package/src/components/Breadcrumbs.astro +1 -1
- package/src/components/CodeComparison.astro +44 -0
- package/src/components/ReferencePage.astro +3 -6
- package/src/components/RulePager.astro +32 -0
- package/src/contracts.ts +39 -10
- package/src/line-diff.ts +55 -0
- package/src/styles/starlight.css +286 -0
- package/src/styles/theme.css +36 -0
package/README.md
CHANGED
|
@@ -8,15 +8,19 @@ Shared Astro and Starlight reference UI for Sarj standards sites
|
|
|
8
8
|
npm install --save-dev @sarj/docs-ui
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Import `@sarj/docs-ui/
|
|
11
|
+
Import `@sarj/docs-ui/starlight.css` once, then compose the typed Astro components. The live component and theme contract is published at [docs-ui.sarj.ai](https://docs-ui.sarj.ai/).
|
|
12
12
|
|
|
13
13
|
Public exports:
|
|
14
14
|
|
|
15
15
|
- `@sarj/docs-ui/Breadcrumbs.astro`
|
|
16
|
+
- `@sarj/docs-ui/CodeComparison.astro`
|
|
16
17
|
- `@sarj/docs-ui/PageAnchor.astro`
|
|
17
18
|
- `@sarj/docs-ui/ReferencePage.astro`
|
|
19
|
+
- `@sarj/docs-ui/RulePager.astro`
|
|
18
20
|
- `@sarj/docs-ui/catalog`
|
|
19
21
|
- `@sarj/docs-ui/contracts`
|
|
22
|
+
- `@sarj/docs-ui/line-diff`
|
|
23
|
+
- `@sarj/docs-ui/starlight.css`
|
|
20
24
|
- `@sarj/docs-ui/styles.css`
|
|
21
25
|
|
|
22
|
-
[Documentation](https://
|
|
26
|
+
[Documentation](https://docs-ui.sarj.ai/) · [Source](https://github.com/sarj-ai/code-standards)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sarj/docs-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"packageManager": "npm@12.0.2",
|
|
5
5
|
"description": "Shared Astro and Starlight reference UI for Sarj standards sites",
|
|
6
6
|
"type": "module",
|
|
@@ -9,10 +9,14 @@
|
|
|
9
9
|
},
|
|
10
10
|
"exports": {
|
|
11
11
|
"./styles.css": "./src/styles/theme.css",
|
|
12
|
+
"./starlight.css": "./src/styles/starlight.css",
|
|
12
13
|
"./catalog": "./src/catalog.ts",
|
|
13
14
|
"./contracts": "./src/contracts.ts",
|
|
15
|
+
"./line-diff": "./src/line-diff.ts",
|
|
16
|
+
"./CodeComparison.astro": "./src/components/CodeComparison.astro",
|
|
14
17
|
"./PageAnchor.astro": "./src/components/PageAnchor.astro",
|
|
15
18
|
"./ReferencePage.astro": "./src/components/ReferencePage.astro",
|
|
19
|
+
"./RulePager.astro": "./src/components/RulePager.astro",
|
|
16
20
|
"./Breadcrumbs.astro": "./src/components/Breadcrumbs.astro"
|
|
17
21
|
},
|
|
18
22
|
"files": [
|
|
@@ -21,7 +25,8 @@
|
|
|
21
25
|
"LICENSE"
|
|
22
26
|
],
|
|
23
27
|
"sideEffects": [
|
|
24
|
-
"./src/styles/theme.css"
|
|
28
|
+
"./src/styles/theme.css",
|
|
29
|
+
"./src/styles/starlight.css"
|
|
25
30
|
],
|
|
26
31
|
"publishConfig": {
|
|
27
32
|
"access": "public"
|
|
@@ -31,13 +36,14 @@
|
|
|
31
36
|
"url": "git+https://github.com/sarj-ai/code-standards.git",
|
|
32
37
|
"directory": "packages/docs-ui"
|
|
33
38
|
},
|
|
34
|
-
"homepage": "https://
|
|
39
|
+
"homepage": "https://docs-ui.sarj.ai/",
|
|
35
40
|
"bugs": {
|
|
36
41
|
"url": "https://github.com/sarj-ai/code-standards/issues"
|
|
37
42
|
},
|
|
38
43
|
"license": "MIT",
|
|
39
44
|
"dependencies": {
|
|
40
|
-
"@fontsource-variable/nunito": "5.3.0"
|
|
45
|
+
"@fontsource-variable/nunito": "5.3.0",
|
|
46
|
+
"diff": "8.0.4"
|
|
41
47
|
},
|
|
42
48
|
"peerDependencies": {
|
|
43
49
|
"@astrojs/starlight": ">=0.41.7 <0.42",
|
package/src/catalog.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BreadcrumbsProps, ReferencePageProps } from './contracts';
|
|
1
|
+
import type { BreadcrumbsProps, CodeComparisonProps, ReferencePageProps, RulePagerProps } from './contracts';
|
|
2
2
|
|
|
3
3
|
export interface ComponentDefinition<Props extends object = object> {
|
|
4
4
|
exportPath: string;
|
|
@@ -9,6 +9,8 @@ export interface ComponentDefinition<Props extends object = object> {
|
|
|
9
9
|
export interface ThemeTokenDefinition {
|
|
10
10
|
cssName: `--sarj-${string}`;
|
|
11
11
|
purpose: string;
|
|
12
|
+
light: `#${string}`;
|
|
13
|
+
dark: `#${string}`;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export const componentCatalog = Object.freeze({
|
|
@@ -20,6 +22,16 @@ export const componentCatalog = Object.freeze({
|
|
|
20
22
|
current: 'Current page label, announced with aria-current="page".',
|
|
21
23
|
},
|
|
22
24
|
} satisfies ComponentDefinition<BreadcrumbsProps>,
|
|
25
|
+
CodeComparison: {
|
|
26
|
+
exportPath: '@sarj/docs-ui/CodeComparison.astro',
|
|
27
|
+
purpose: 'Compare independently readable before and after source with accessible change cues.',
|
|
28
|
+
properties: {
|
|
29
|
+
id: 'Stable identifier used to connect the comparison and its heading.',
|
|
30
|
+
title: 'Optional scenario heading shared by both sides.',
|
|
31
|
+
before: 'Rejected or prior source snapshot and its presentation metadata.',
|
|
32
|
+
after: 'Preferred or resulting source snapshot and its presentation metadata.',
|
|
33
|
+
},
|
|
34
|
+
} satisfies ComponentDefinition<CodeComparisonProps>,
|
|
23
35
|
PageAnchor: {
|
|
24
36
|
exportPath: '@sarj/docs-ui/PageAnchor.astro',
|
|
25
37
|
purpose: 'Provide the focusable top anchor used by Starlight reference pages.',
|
|
@@ -27,25 +39,31 @@ export const componentCatalog = Object.freeze({
|
|
|
27
39
|
} satisfies ComponentDefinition,
|
|
28
40
|
ReferencePage: {
|
|
29
41
|
exportPath: '@sarj/docs-ui/ReferencePage.astro',
|
|
30
|
-
purpose: 'Render a Starlight reference shell with explicit
|
|
42
|
+
purpose: 'Render a Starlight reference shell with explicit robots behavior.',
|
|
31
43
|
properties: {
|
|
32
44
|
title: 'Document title and primary accessible page identity.',
|
|
33
45
|
description: 'Concise page description used by document metadata.',
|
|
34
46
|
sidebar: 'Starlight sidebar definition for this reference surface.',
|
|
35
|
-
searchable: 'Whether Pagefind indexes the page body; defaults to true.',
|
|
36
47
|
indexable: 'Whether robots may index the public page; defaults to true.',
|
|
37
|
-
discovery: 'Deprecated shorthand retained for compatibility; never access control.',
|
|
38
48
|
hasSidebar: 'Whether the Starlight sidebar is rendered; defaults to true.',
|
|
39
49
|
template: 'Starlight document or splash template; defaults to doc.',
|
|
40
50
|
},
|
|
41
51
|
} satisfies ComponentDefinition<ReferencePageProps>,
|
|
52
|
+
RulePager: {
|
|
53
|
+
exportPath: '@sarj/docs-ui/RulePager.astro',
|
|
54
|
+
purpose: 'Navigate between adjacent rules with accessible links and guarded arrow-key shortcuts.',
|
|
55
|
+
properties: {
|
|
56
|
+
previous: 'Optional previous rule link and label.',
|
|
57
|
+
next: 'Optional next rule link and label.',
|
|
58
|
+
},
|
|
59
|
+
} satisfies ComponentDefinition<RulePagerProps>,
|
|
42
60
|
});
|
|
43
61
|
|
|
44
62
|
export const themeTokenCatalog = Object.freeze([
|
|
45
|
-
{ cssName: '--sarj-color-report', purpose: 'Diagnostic and rejection emphasis.' },
|
|
46
|
-
{ cssName: '--sarj-color-pass', purpose: 'Accepted example and success emphasis.' },
|
|
47
|
-
{ cssName: '--sarj-color-warning', purpose: 'Warning-level diagnostic emphasis with AA text contrast.' },
|
|
48
|
-
{ cssName: '--sarj-color-rule', purpose: 'Structural borders and separators.' },
|
|
49
|
-
{ cssName: '--sarj-color-paper', purpose: 'Reference surface background.' },
|
|
50
|
-
{ cssName: '--sarj-color-ink', purpose: 'Primary reference text.' },
|
|
63
|
+
{ cssName: '--sarj-color-report', purpose: 'Diagnostic and rejection emphasis.', light: '#c34453', dark: '#ff8794' },
|
|
64
|
+
{ cssName: '--sarj-color-pass', purpose: 'Accepted example and success emphasis.', light: '#16806d', dark: '#62d5bd' },
|
|
65
|
+
{ cssName: '--sarj-color-warning', purpose: 'Warning-level diagnostic emphasis with AA text contrast.', light: '#986a00', dark: '#a97708' },
|
|
66
|
+
{ cssName: '--sarj-color-rule', purpose: 'Structural borders and separators.', light: '#e4e6eb', dark: '#272a32' },
|
|
67
|
+
{ cssName: '--sarj-color-paper', purpose: 'Reference surface background.', light: '#ffffff', dark: '#0b0c10' },
|
|
68
|
+
{ cssName: '--sarj-color-ink', purpose: 'Primary reference text.', light: '#12141a', dark: '#f4f5f8' },
|
|
51
69
|
] satisfies readonly ThemeTokenDefinition[]);
|
|
@@ -4,7 +4,7 @@ import type { BreadcrumbsProps as Props } from '../contracts';
|
|
|
4
4
|
const { ancestors, current } = Astro.props;
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
<nav class="breadcrumbs" aria-label="Breadcrumb">
|
|
7
|
+
<nav class="breadcrumbs sarj-breadcrumbs" aria-label="Breadcrumb">
|
|
8
8
|
<ol>
|
|
9
9
|
{ancestors.map((ancestor) => <li><a href={ancestor.href}>{ancestor.label}</a></li>)}
|
|
10
10
|
<li><span aria-current="page">{current}</span></li>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Code } from '@astrojs/starlight/components';
|
|
3
|
+
|
|
4
|
+
import type { CodeComparisonFile, CodeComparisonProps as Props, CodeComparisonSide } from '../contracts';
|
|
5
|
+
|
|
6
|
+
const { id, title, before, after } = Astro.props;
|
|
7
|
+
const headingId = `${id}-title`;
|
|
8
|
+
|
|
9
|
+
function files(side: CodeComparisonSide): readonly CodeComparisonFile[] {
|
|
10
|
+
return side.files.filter((file) => file.source.trim().length > 0);
|
|
11
|
+
}
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<section
|
|
15
|
+
class="sarj-code-comparison"
|
|
16
|
+
aria-label={title === undefined ? 'Before and after code comparison' : undefined}
|
|
17
|
+
aria-labelledby={title === undefined ? undefined : headingId}
|
|
18
|
+
data-code-comparison={id}
|
|
19
|
+
>
|
|
20
|
+
{title !== undefined && <h3 id={headingId} class="sarj-code-comparison__title">{title}</h3>}
|
|
21
|
+
<div class="sarj-code-comparison__grid">
|
|
22
|
+
{[{ side: before, kind: 'before' }, { side: after, kind: 'after' }].map(({ side, kind }) => (
|
|
23
|
+
<figure class:list={['sarj-code-comparison__side', `sarj-code-comparison__side--${kind}`]}>
|
|
24
|
+
<figcaption>
|
|
25
|
+
<span>{side.label}</span>
|
|
26
|
+
{side.title !== undefined && <strong>{side.title}</strong>}
|
|
27
|
+
</figcaption>
|
|
28
|
+
<div class="sarj-code-comparison__files">
|
|
29
|
+
{files(side).map((file) => (
|
|
30
|
+
<Code
|
|
31
|
+
code={file.source}
|
|
32
|
+
lang={file.language}
|
|
33
|
+
title={file.title}
|
|
34
|
+
frame={file.title === undefined ? 'none' : 'code'}
|
|
35
|
+
del={kind === 'before' ? file.marks : undefined}
|
|
36
|
+
ins={kind === 'after' ? file.marks : undefined}
|
|
37
|
+
wrap
|
|
38
|
+
/>
|
|
39
|
+
))}
|
|
40
|
+
</div>
|
|
41
|
+
</figure>
|
|
42
|
+
))}
|
|
43
|
+
</div>
|
|
44
|
+
</section>
|
|
@@ -6,14 +6,11 @@ const {
|
|
|
6
6
|
title,
|
|
7
7
|
description,
|
|
8
8
|
sidebar,
|
|
9
|
-
searchable: searchableProp,
|
|
10
9
|
indexable: indexableProp,
|
|
11
|
-
discovery,
|
|
12
10
|
hasSidebar = true,
|
|
13
11
|
template = 'doc',
|
|
14
12
|
} = Astro.props;
|
|
15
|
-
const
|
|
16
|
-
const indexable = indexableProp ?? discovery !== 'unlisted';
|
|
13
|
+
const indexable = indexableProp ?? true;
|
|
17
14
|
---
|
|
18
15
|
|
|
19
16
|
<StarlightPage
|
|
@@ -21,7 +18,7 @@ const indexable = indexableProp ?? discovery !== 'unlisted';
|
|
|
21
18
|
title,
|
|
22
19
|
description,
|
|
23
20
|
template,
|
|
24
|
-
pagefind:
|
|
21
|
+
pagefind: false,
|
|
25
22
|
prev: false,
|
|
26
23
|
next: false,
|
|
27
24
|
head: indexable ? [] : [{ tag: 'meta', attrs: { name: 'robots', content: 'noindex, nofollow' } }],
|
|
@@ -29,7 +26,7 @@ const indexable = indexableProp ?? discovery !== 'unlisted';
|
|
|
29
26
|
sidebar={sidebar}
|
|
30
27
|
hasSidebar={hasSidebar}
|
|
31
28
|
>
|
|
32
|
-
<div
|
|
29
|
+
<div>
|
|
33
30
|
<slot />
|
|
34
31
|
</div>
|
|
35
32
|
</StarlightPage>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { RulePagerProps as Props } from '../contracts';
|
|
3
|
+
|
|
4
|
+
const { previous, next } = Astro.props;
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
<nav class="sarj-rule-pager" aria-label="Rule navigation">
|
|
8
|
+
{previous && (
|
|
9
|
+
<a href={previous.href} rel="prev" data-rule-previous aria-keyshortcuts="ArrowLeft" aria-label={`Previous rule: ${previous.label}`} title={previous.label}>
|
|
10
|
+
<kbd aria-hidden="true">←</kbd><span>Previous</span>
|
|
11
|
+
</a>
|
|
12
|
+
)}
|
|
13
|
+
{next && (
|
|
14
|
+
<a href={next.href} rel="next" data-rule-next aria-keyshortcuts="ArrowRight" aria-label={`Next rule: ${next.label}`} title={next.label}>
|
|
15
|
+
<span>Next</span><kbd aria-hidden="true">→</kbd>
|
|
16
|
+
</a>
|
|
17
|
+
)}
|
|
18
|
+
</nav>
|
|
19
|
+
|
|
20
|
+
<script>
|
|
21
|
+
document.addEventListener('keydown', (event) => {
|
|
22
|
+
if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
|
|
23
|
+
if (event.target instanceof Element && event.target.closest('a, button, input, select, textarea, summary, [contenteditable]')) return;
|
|
24
|
+
const selector = event.key === 'ArrowLeft' ? '[data-rule-previous]' : event.key === 'ArrowRight' ? '[data-rule-next]' : null;
|
|
25
|
+
if (!selector) return;
|
|
26
|
+
const link = document.querySelector(selector);
|
|
27
|
+
if (link instanceof HTMLAnchorElement) {
|
|
28
|
+
event.preventDefault();
|
|
29
|
+
location.assign(link.href);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
</script>
|
package/src/contracts.ts
CHANGED
|
@@ -12,25 +12,54 @@ export interface BreadcrumbsProps {
|
|
|
12
12
|
current: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
/** A one-based inclusive range understood by Expressive Code text markers. */
|
|
16
|
+
export interface CodeLineMark {
|
|
17
|
+
range: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** One source file rendered inside a comparison side. */
|
|
21
|
+
export interface CodeComparisonFile {
|
|
22
|
+
source: string;
|
|
23
|
+
language: string;
|
|
24
|
+
title?: string;
|
|
25
|
+
marks?: CodeLineMark;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** One independently readable state in a code comparison. */
|
|
29
|
+
export interface CodeComparisonSide {
|
|
30
|
+
label: string;
|
|
31
|
+
title?: string;
|
|
32
|
+
files: readonly CodeComparisonFile[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Public properties accepted by {@link CodeComparison}. */
|
|
36
|
+
export interface CodeComparisonProps {
|
|
37
|
+
id: string;
|
|
38
|
+
title?: string;
|
|
39
|
+
before: CodeComparisonSide;
|
|
40
|
+
after: CodeComparisonSide;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface RulePagerLink {
|
|
44
|
+
href: string;
|
|
45
|
+
label: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Public properties accepted by {@link RulePager}. */
|
|
49
|
+
export interface RulePagerProps {
|
|
50
|
+
previous?: RulePagerLink | null;
|
|
51
|
+
next?: RulePagerLink | null;
|
|
52
|
+
}
|
|
16
53
|
|
|
17
|
-
|
|
18
|
-
* Legacy discovery shorthand. `unlisted` affects search and robots metadata;
|
|
19
|
-
* it is not access control and the rendered page remains public.
|
|
20
|
-
*/
|
|
21
|
-
export type ReferenceDiscovery = 'searchable' | 'navigation-only' | 'unlisted';
|
|
54
|
+
export type ReferenceSidebar = NonNullable<StarlightUserConfig['sidebar']>;
|
|
22
55
|
|
|
23
56
|
/** Public properties accepted by {@link ReferencePage}. */
|
|
24
57
|
export interface ReferencePageProps {
|
|
25
58
|
title: string;
|
|
26
59
|
description: string;
|
|
27
60
|
sidebar: ReferenceSidebar;
|
|
28
|
-
/** Include the page body in Pagefind. Defaults to true. */
|
|
29
|
-
searchable?: boolean;
|
|
30
61
|
/** Allow search engines to index the page. Defaults to true. */
|
|
31
62
|
indexable?: boolean;
|
|
32
|
-
/** @deprecated Prefer the explicit `searchable` and `indexable` properties. */
|
|
33
|
-
discovery?: ReferenceDiscovery;
|
|
34
63
|
hasSidebar?: boolean;
|
|
35
64
|
template?: 'doc' | 'splash';
|
|
36
65
|
}
|
package/src/line-diff.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { diffLines } from 'diff';
|
|
2
|
+
|
|
3
|
+
import type { CodeLineMark } from './contracts';
|
|
4
|
+
|
|
5
|
+
export interface ChangedLineMarks {
|
|
6
|
+
before: CodeLineMark | undefined;
|
|
7
|
+
after: CodeLineMark | undefined;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Return presentation-only line markers for two explicitly related sources. */
|
|
11
|
+
export function changedLineMarks(before: string, after: string): ChangedLineMarks {
|
|
12
|
+
const beforeLines: number[] = [];
|
|
13
|
+
const afterLines: number[] = [];
|
|
14
|
+
let beforeLine = 1;
|
|
15
|
+
let afterLine = 1;
|
|
16
|
+
|
|
17
|
+
for (const change of diffLines(before, after)) {
|
|
18
|
+
const count = change.count;
|
|
19
|
+
if (change.removed) {
|
|
20
|
+
addLines(beforeLines, beforeLine, count);
|
|
21
|
+
beforeLine += count;
|
|
22
|
+
} else if (change.added) {
|
|
23
|
+
addLines(afterLines, afterLine, count);
|
|
24
|
+
afterLine += count;
|
|
25
|
+
} else {
|
|
26
|
+
beforeLine += count;
|
|
27
|
+
afterLine += count;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { before: lineMark(beforeLines), after: lineMark(afterLines) };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function addLines(output: number[], first: number, count: number): void {
|
|
35
|
+
for (let offset = 0; offset < count; offset += 1) output.push(first + offset);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function lineMark(lines: readonly number[]): CodeLineMark | undefined {
|
|
39
|
+
if (lines.length === 0) return undefined;
|
|
40
|
+
const ranges: string[] = [];
|
|
41
|
+
let first = lines[0] ?? 1;
|
|
42
|
+
let last = first;
|
|
43
|
+
|
|
44
|
+
for (const line of lines.slice(1)) {
|
|
45
|
+
if (line === last + 1) {
|
|
46
|
+
last = line;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
ranges.push(first === last ? String(first) : `${String(first)}-${String(last)}`);
|
|
50
|
+
first = line;
|
|
51
|
+
last = line;
|
|
52
|
+
}
|
|
53
|
+
ranges.push(first === last ? String(first) : `${String(first)}-${String(last)}`);
|
|
54
|
+
return { range: ranges.join(',') };
|
|
55
|
+
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
@import './theme.css';
|
|
2
|
+
|
|
3
|
+
html {
|
|
4
|
+
scroll-behavior: smooth;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body,
|
|
8
|
+
.content-panel,
|
|
9
|
+
.main-pane,
|
|
10
|
+
.right-sidebar-panel,
|
|
11
|
+
.sidebar-pane {
|
|
12
|
+
background-color: var(--sl-color-black);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
body {
|
|
16
|
+
font-weight: 450;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.main-pane,
|
|
20
|
+
.main-frame,
|
|
21
|
+
.sl-container {
|
|
22
|
+
min-width: 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.main-pane {
|
|
26
|
+
--sl-content-width: 100%;
|
|
27
|
+
width: 100%;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.content-panel:has(> .reference-page-anchor:only-child) {
|
|
31
|
+
display: none;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.page > header.header {
|
|
35
|
+
border-bottom: 1px solid var(--sarj-color-rule);
|
|
36
|
+
box-shadow: none;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.page > header.header > .header {
|
|
40
|
+
border-bottom: 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.sidebar-content a,
|
|
44
|
+
.sidebar-content summary,
|
|
45
|
+
.social-icons a,
|
|
46
|
+
.header button,
|
|
47
|
+
starlight-menu-button button {
|
|
48
|
+
min-width: 44px;
|
|
49
|
+
min-height: 44px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.sidebar-content a,
|
|
53
|
+
.sidebar-content summary {
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.sidebar-content a[aria-current='page'],
|
|
59
|
+
.sidebar-content a[aria-current='page']:hover,
|
|
60
|
+
.sidebar-content a[aria-current='page']:focus {
|
|
61
|
+
background: var(--sl-color-accent-low);
|
|
62
|
+
color: var(--sl-color-accent-high);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
:where(.sl-markdown-content, main) :is(h1, h2, h3) {
|
|
66
|
+
color: var(--sl-color-white);
|
|
67
|
+
font-weight: 700;
|
|
68
|
+
letter-spacing: -0.035em;
|
|
69
|
+
text-wrap: balance;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
:where(.sl-markdown-content, main) h1 {
|
|
73
|
+
font-size: clamp(1.8rem, 3.5vw, 2.35rem);
|
|
74
|
+
line-height: 1.08;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.sarj-about-page {
|
|
78
|
+
width: min(100%, 48rem);
|
|
79
|
+
padding-block: 1.25rem 3rem;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.sarj-about-page > h1 {
|
|
83
|
+
margin-block: 0 0.75rem;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.sarj-about-page > p {
|
|
87
|
+
max-width: 42rem;
|
|
88
|
+
color: var(--sl-color-gray-2);
|
|
89
|
+
font-size: 1.05rem;
|
|
90
|
+
line-height: 1.65;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.sarj-related-standards {
|
|
94
|
+
margin-top: 3rem;
|
|
95
|
+
padding-top: 1.5rem;
|
|
96
|
+
border-top: 1px solid var(--sarj-color-rule);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.sarj-related-standards h2 {
|
|
100
|
+
margin: 0 0 0.5rem;
|
|
101
|
+
font-size: 1rem;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.sarj-breadcrumbs {
|
|
105
|
+
margin-block: 0 1.25rem;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.sarj-breadcrumbs ol {
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
flex-wrap: wrap;
|
|
112
|
+
gap: 0.45rem;
|
|
113
|
+
margin: 0;
|
|
114
|
+
padding: 0;
|
|
115
|
+
color: var(--sl-color-gray-2);
|
|
116
|
+
font-size: 0.85rem;
|
|
117
|
+
list-style: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.sarj-breadcrumbs li + li::before {
|
|
121
|
+
margin-inline-end: 0.45rem;
|
|
122
|
+
color: var(--sl-color-gray-4);
|
|
123
|
+
content: '/';
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.sarj-breadcrumbs a {
|
|
127
|
+
display: inline-flex;
|
|
128
|
+
min-height: 44px;
|
|
129
|
+
align-items: center;
|
|
130
|
+
color: inherit;
|
|
131
|
+
text-decoration: underline;
|
|
132
|
+
text-underline-offset: 0.2em;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.sarj-breadcrumbs [aria-current='page'] {
|
|
136
|
+
color: var(--sl-color-white);
|
|
137
|
+
font-weight: 650;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.sarj-code-comparison {
|
|
141
|
+
min-width: 0;
|
|
142
|
+
max-width: 100%;
|
|
143
|
+
container-type: inline-size;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.sarj-code-comparison__title {
|
|
147
|
+
margin: 0 0 0.5rem;
|
|
148
|
+
font-size: 0.95rem;
|
|
149
|
+
line-height: 1.35;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.sarj-code-comparison__grid {
|
|
153
|
+
display: grid;
|
|
154
|
+
min-width: 0;
|
|
155
|
+
gap: 1rem;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.sarj-code-comparison__side {
|
|
159
|
+
display: flex;
|
|
160
|
+
min-width: 0;
|
|
161
|
+
max-width: 100%;
|
|
162
|
+
margin: 0;
|
|
163
|
+
flex-direction: column;
|
|
164
|
+
border: 1px solid var(--sarj-color-rule);
|
|
165
|
+
border-radius: 0.55rem;
|
|
166
|
+
background: var(--sl-color-black);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.sarj-code-comparison__side--before {
|
|
170
|
+
border-top-color: var(--sarj-color-report);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.sarj-code-comparison__side--after {
|
|
174
|
+
border-top-color: var(--sarj-color-pass);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.sarj-code-comparison__side figcaption {
|
|
178
|
+
display: grid;
|
|
179
|
+
gap: 0.15rem;
|
|
180
|
+
padding: 0.75rem 0.9rem;
|
|
181
|
+
border-bottom: 1px solid var(--sarj-color-rule);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.sarj-code-comparison__side figcaption span {
|
|
185
|
+
color: var(--sl-color-gray-2);
|
|
186
|
+
font: 700 0.68rem/1.4 var(--sl-font-mono);
|
|
187
|
+
letter-spacing: 0.08em;
|
|
188
|
+
text-transform: uppercase;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.sarj-code-comparison__side figcaption strong {
|
|
192
|
+
font-size: 1rem;
|
|
193
|
+
line-height: 1.35;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.sarj-code-comparison__files,
|
|
197
|
+
.sarj-code-comparison__files > .expressive-code {
|
|
198
|
+
min-width: 0;
|
|
199
|
+
max-width: 100%;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.sarj-code-comparison__files {
|
|
203
|
+
display: flex;
|
|
204
|
+
flex: 1;
|
|
205
|
+
flex-direction: column;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.sarj-code-comparison__files > .expressive-code {
|
|
209
|
+
margin: 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.sarj-code-comparison__files .expressive-code figure {
|
|
213
|
+
border: 0;
|
|
214
|
+
border-top: 1px solid var(--sarj-color-rule);
|
|
215
|
+
border-radius: 0;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.sarj-code-comparison__files .expressive-code figcaption.header:empty {
|
|
219
|
+
display: none;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.sarj-code-comparison__files pre {
|
|
223
|
+
max-width: 100%;
|
|
224
|
+
overflow: auto;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
*:focus-visible {
|
|
228
|
+
outline: 3px solid var(--sl-color-accent);
|
|
229
|
+
outline-offset: 3px;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
@container (min-width: 52rem) {
|
|
233
|
+
.sarj-code-comparison__grid {
|
|
234
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
235
|
+
align-items: stretch;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.sarj-rule-pager {
|
|
240
|
+
display: flex;
|
|
241
|
+
flex: 0 0 auto;
|
|
242
|
+
gap: 0.35rem;
|
|
243
|
+
margin: 0;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.sarj-rule-pager a {
|
|
247
|
+
display: inline-flex;
|
|
248
|
+
min-height: 44px;
|
|
249
|
+
align-items: center;
|
|
250
|
+
gap: 0.3rem;
|
|
251
|
+
padding: 0.35rem 0.55rem;
|
|
252
|
+
border: 1px solid var(--sarj-color-rule);
|
|
253
|
+
border-radius: 0.4rem;
|
|
254
|
+
color: var(--sl-color-white);
|
|
255
|
+
font-size: 0.75rem;
|
|
256
|
+
text-decoration: none;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.sarj-rule-pager a:hover,
|
|
260
|
+
.sarj-rule-pager a:focus-visible {
|
|
261
|
+
border-color: var(--sl-color-accent);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.sarj-rule-pager kbd {
|
|
265
|
+
padding: 0.05rem 0.28rem;
|
|
266
|
+
border: 1px solid var(--sarj-color-rule);
|
|
267
|
+
border-radius: 0.2rem;
|
|
268
|
+
background: var(--sl-color-gray-6);
|
|
269
|
+
font-family: var(--sl-font-mono);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
@media (max-width: 22rem) {
|
|
273
|
+
.sarj-rule-pager a { width: 44px; justify-content: center; padding-inline: 0; }
|
|
274
|
+
.sarj-rule-pager a span { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
@media (prefers-reduced-motion: reduce) {
|
|
278
|
+
:root { scroll-behavior: auto; }
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@media (forced-colors: active) {
|
|
282
|
+
.sarj-code-comparison__side--before,
|
|
283
|
+
.sarj-code-comparison__side--after {
|
|
284
|
+
border-top-width: 3px;
|
|
285
|
+
}
|
|
286
|
+
}
|
package/src/styles/theme.css
CHANGED
|
@@ -48,3 +48,39 @@
|
|
|
48
48
|
--sarj-color-paper: #0b0c10;
|
|
49
49
|
--sarj-color-ink: #f4f5f8;
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
.breadcrumbs ol {
|
|
53
|
+
display: flex;
|
|
54
|
+
flex-wrap: wrap;
|
|
55
|
+
gap: 0.35rem;
|
|
56
|
+
margin: 0;
|
|
57
|
+
padding: 0;
|
|
58
|
+
list-style: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.breadcrumbs li {
|
|
62
|
+
display: inline-flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 0.35rem;
|
|
65
|
+
min-width: 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.breadcrumbs li + li::before {
|
|
69
|
+
color: var(--sl-color-gray-3);
|
|
70
|
+
content: '/';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.breadcrumbs a,
|
|
74
|
+
.breadcrumbs [aria-current='page'] {
|
|
75
|
+
overflow-wrap: anywhere;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.breadcrumbs a {
|
|
79
|
+
border-radius: 0.2rem;
|
|
80
|
+
color: var(--sl-color-accent-high);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.breadcrumbs a:focus-visible {
|
|
84
|
+
outline: 2px solid var(--sl-color-accent);
|
|
85
|
+
outline-offset: 3px;
|
|
86
|
+
}
|