@signal9/era-ui 2.26.0 → 2.27.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/dist/docs/button.md +1 -0
- package/dist/docs/css-utilities.d.ts +21 -0
- package/dist/docs/css-utilities.js +124 -0
- package/dist/docs/generated-docs.d.ts +2 -0
- package/dist/docs/generated-docs.js +7 -0
- package/dist/docs/index.js +4 -0
- package/dist/docs/llms-txt.js +17 -1
- package/dist/docs/scroll-area.md +7 -0
- package/dist/docs/utilities.d.ts +4 -0
- package/dist/docs/utilities.js +15 -0
- package/dist/era-ui.css +1 -1
- package/dist/generated-docs/button.md +1 -0
- package/dist/generated-docs/llms-full.txt +224 -1
- package/dist/generated-docs/llms.txt +15 -1
- package/dist/generated-docs/manifest.json +72 -3
- package/dist/generated-docs/scroll-area.md +8 -0
- package/dist/generated-docs/utilities.json +53 -0
- package/dist/generated-docs/utilities.md +195 -0
- package/dist/styles/index.css +25 -5
- package/dist/styles/surfaces/glass.css +5 -1
- package/package.json +3 -2
- package/skill/SKILL.md +17 -0
package/dist/docs/button.md
CHANGED
|
@@ -15,4 +15,5 @@
|
|
|
15
15
|
|
|
16
16
|
- Heights, radii, paddings, and gaps come from density tokens — never hard-code px.
|
|
17
17
|
- `link` variant intentionally does not change color on hover (underline only) so it reads as static prose until confirmed interactive.
|
|
18
|
+
- `variant="link"` is for a _control_ that should look like a link (a button that reveals a panel, a destructive action in a row). For a link in body copy or prose, use the `era-link` CSS utility on an `<a>` instead — see [Utilities](/utilities.md).
|
|
18
19
|
- For a segmented row of buttons, use `ButtonGroup`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface CssUtility {
|
|
2
|
+
/** Class name, e.g. `era-link`. */
|
|
3
|
+
name: string;
|
|
4
|
+
/** Stylesheet the utility is declared in, relative to src/lib/styles. */
|
|
5
|
+
file: string;
|
|
6
|
+
/** One-line use case from the comment's `@use` line, if present. */
|
|
7
|
+
useCase?: string;
|
|
8
|
+
/** The rest of the doc comment — rationale, caveats, axis behaviour. */
|
|
9
|
+
description: string;
|
|
10
|
+
/** The `@utility` block verbatim, as authored. */
|
|
11
|
+
css: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* @param sources stylesheet text keyed by path relative to src/lib/styles
|
|
15
|
+
* (e.g. `index.css`, `surfaces/glass.css`).
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseUtilities(sources: Record<string, string>): CssUtility[];
|
|
18
|
+
/** Renders the utilities reference — the generated `utilities.md` body. */
|
|
19
|
+
export declare function buildUtilitiesDoc(utilities: CssUtility[]): string;
|
|
20
|
+
/** One line per utility for llms.txt — name plus its searchable use case. */
|
|
21
|
+
export declare function buildUtilitiesIndex(utilities: CssUtility[]): string[];
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Extracts era's Tailwind v4 `@utility` classes — era-link, era-text-trim,
|
|
3
|
+
* era-shimmer, scrollbar-none, glass-blur, … — from the stylesheet source.
|
|
4
|
+
*
|
|
5
|
+
* These are first-class API but they have no JS export and no .svelte file, so
|
|
6
|
+
* nothing else in the doc pipeline can see them: the component barrel misses
|
|
7
|
+
* them, the registry misses them, and a consumer (or an agent) reasoning from
|
|
8
|
+
* "what does era expose for links?" never finds `era-link`. The doc comment
|
|
9
|
+
* above each `@utility` is already thorough, so it — not a hand-maintained list
|
|
10
|
+
* — is the source of truth. This parses it.
|
|
11
|
+
*
|
|
12
|
+
* The convention the parser leans on: an `@use ` line in the comment is the
|
|
13
|
+
* one-line use case (what an agent searches), and the remaining prose is the
|
|
14
|
+
* long description. A utility with no `@use` line still documents fine; it just
|
|
15
|
+
* lands in the docs without a use-case summary.
|
|
16
|
+
*
|
|
17
|
+
* Pure (takes sources, returns data) so both the runtime glob loader and the
|
|
18
|
+
* node build script can drive it — same split as component-source-scanners.ts.
|
|
19
|
+
*/
|
|
20
|
+
/** Files whose utilities are library API. Order fixes the doc's order. */
|
|
21
|
+
const FILE_ORDER = ['index.css', 'surfaces/glass.css'];
|
|
22
|
+
function cleanComment(raw) {
|
|
23
|
+
return raw
|
|
24
|
+
.split('\n')
|
|
25
|
+
.map((line) => line.replace(/^\s*\*\s?/, '').trimEnd())
|
|
26
|
+
.join('\n')
|
|
27
|
+
.trim();
|
|
28
|
+
}
|
|
29
|
+
/** Split the comment into its `@use` one-liner and the surrounding prose. */
|
|
30
|
+
function splitUseCase(comment) {
|
|
31
|
+
const lines = comment.split('\n');
|
|
32
|
+
const start = lines.findIndex((line) => line.startsWith('@use '));
|
|
33
|
+
if (start === -1)
|
|
34
|
+
return { description: comment.trim() };
|
|
35
|
+
let end = start + 1;
|
|
36
|
+
while (end < lines.length && lines[end].trim() !== '')
|
|
37
|
+
end++;
|
|
38
|
+
const useCase = lines
|
|
39
|
+
.slice(start, end)
|
|
40
|
+
.join(' ')
|
|
41
|
+
.replace(/^@use\s+/, '')
|
|
42
|
+
.replace(/\s+/g, ' ')
|
|
43
|
+
.trim();
|
|
44
|
+
const description = [...lines.slice(0, start), ...lines.slice(end)].join('\n').trim();
|
|
45
|
+
return { useCase, description };
|
|
46
|
+
}
|
|
47
|
+
/** Walk from the opening brace to its match so nested rules survive intact. */
|
|
48
|
+
function blockEnd(source, openBrace) {
|
|
49
|
+
let depth = 0;
|
|
50
|
+
for (let i = openBrace; i < source.length; i++) {
|
|
51
|
+
if (source[i] === '{')
|
|
52
|
+
depth++;
|
|
53
|
+
else if (source[i] === '}' && --depth === 0)
|
|
54
|
+
return i;
|
|
55
|
+
}
|
|
56
|
+
return source.length - 1;
|
|
57
|
+
}
|
|
58
|
+
function parseFile(file, source) {
|
|
59
|
+
const out = [];
|
|
60
|
+
// The comment body may not contain a terminator — a lazy `[\s\S]*?` would
|
|
61
|
+
// happily start at the first comment in the file and run through every `*/`
|
|
62
|
+
// between there and the @utility.
|
|
63
|
+
const pattern = /\/\*((?:(?!\*\/)[\s\S])*)\*\/\s*@utility\s+([\w-]+)\s*\{/g;
|
|
64
|
+
for (const match of source.matchAll(pattern)) {
|
|
65
|
+
const openBrace = match.index + match[0].length - 1;
|
|
66
|
+
const end = blockEnd(source, openBrace);
|
|
67
|
+
const { useCase, description } = splitUseCase(cleanComment(match[1]));
|
|
68
|
+
out.push({
|
|
69
|
+
name: match[2],
|
|
70
|
+
file,
|
|
71
|
+
useCase,
|
|
72
|
+
description,
|
|
73
|
+
css: source.slice(source.indexOf('@utility', match.index), end + 1)
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* @param sources stylesheet text keyed by path relative to src/lib/styles
|
|
80
|
+
* (e.g. `index.css`, `surfaces/glass.css`).
|
|
81
|
+
*/
|
|
82
|
+
export function parseUtilities(sources) {
|
|
83
|
+
const rank = (file) => {
|
|
84
|
+
const i = FILE_ORDER.indexOf(file);
|
|
85
|
+
return i === -1 ? FILE_ORDER.length : i;
|
|
86
|
+
};
|
|
87
|
+
return Object.entries(sources)
|
|
88
|
+
.sort(([a], [b]) => rank(a) - rank(b) || a.localeCompare(b))
|
|
89
|
+
.flatMap(([file, source]) => parseFile(file, source));
|
|
90
|
+
}
|
|
91
|
+
const INTRO = [
|
|
92
|
+
'era ships a handful of Tailwind v4 `@utility` classes for the patterns that are',
|
|
93
|
+
'styling, not components — an inline link, ink-centred control text, a hidden',
|
|
94
|
+
'scrollbar. They need no import: they ride along with the stylesheet.',
|
|
95
|
+
'',
|
|
96
|
+
'```ts',
|
|
97
|
+
'import "@sig-nine/era-ui/css";',
|
|
98
|
+
'```',
|
|
99
|
+
'',
|
|
100
|
+
'```svelte',
|
|
101
|
+
'<a class="era-link" href="/spacing">the spacing ladder</a>',
|
|
102
|
+
'```'
|
|
103
|
+
].join('\n');
|
|
104
|
+
/** Renders the utilities reference — the generated `utilities.md` body. */
|
|
105
|
+
export function buildUtilitiesDoc(utilities) {
|
|
106
|
+
const lines = ['## Overview', '', INTRO, '', '| Utility | Use it for |', '|---|---|'];
|
|
107
|
+
for (const u of utilities) {
|
|
108
|
+
lines.push(`| \`${u.name}\` | ${u.useCase ?? '—'} |`);
|
|
109
|
+
}
|
|
110
|
+
lines.push('');
|
|
111
|
+
for (const u of utilities) {
|
|
112
|
+
lines.push(`## ${u.name}`, '');
|
|
113
|
+
if (u.useCase)
|
|
114
|
+
lines.push(`**Use it for:** ${u.useCase}`, '');
|
|
115
|
+
if (u.description)
|
|
116
|
+
lines.push(u.description, '');
|
|
117
|
+
lines.push(`Declared in \`${u.file}\`.`, '', '```css', u.css, '```', '');
|
|
118
|
+
}
|
|
119
|
+
return lines.join('\n');
|
|
120
|
+
}
|
|
121
|
+
/** One line per utility for llms.txt — name plus its searchable use case. */
|
|
122
|
+
export function buildUtilitiesIndex(utilities) {
|
|
123
|
+
return utilities.map((u) => `- \`${u.name}\` — ${u.useCase ?? u.description.split('\n')[0]}`);
|
|
124
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export declare const docsBySlug: Record<string, string>;
|
|
2
2
|
export declare const knownSlugs: Set<string>;
|
|
3
|
+
/** Served verbatim at /utilities.json so tooling can enumerate the CSS API. */
|
|
4
|
+
export declare const utilitiesJson: string;
|
|
3
5
|
export declare const llmsTxtTemplate: string;
|
|
4
6
|
export declare const llmsFullTxt: string;
|
|
@@ -12,6 +12,11 @@ const manifestModules = import.meta.glob('../generated-docs/manifest.json', {
|
|
|
12
12
|
import: 'default',
|
|
13
13
|
eager: true
|
|
14
14
|
});
|
|
15
|
+
const utilitiesModules = import.meta.glob('../generated-docs/utilities.json', {
|
|
16
|
+
query: '?raw',
|
|
17
|
+
import: 'default',
|
|
18
|
+
eager: true
|
|
19
|
+
});
|
|
15
20
|
function stem(path) {
|
|
16
21
|
return path.replace(/^\.\.\/generated-docs\//, '').replace(/\.(md|txt)$/, '');
|
|
17
22
|
}
|
|
@@ -19,5 +24,7 @@ export const docsBySlug = Object.fromEntries(Object.entries(markdownFiles).map((
|
|
|
19
24
|
const manifest = Object.values(manifestModules)[0];
|
|
20
25
|
const manifestSlugs = manifest?.entries?.map((entry) => entry.slug) ?? [];
|
|
21
26
|
export const knownSlugs = new Set(manifestSlugs.length ? manifestSlugs : Object.keys(docsBySlug));
|
|
27
|
+
/** Served verbatim at /utilities.json so tooling can enumerate the CSS API. */
|
|
28
|
+
export const utilitiesJson = utilitiesModules['../generated-docs/utilities.json'] ?? '';
|
|
22
29
|
export const llmsTxtTemplate = textFiles['../generated-docs/llms.txt'] ?? '';
|
|
23
30
|
export const llmsFullTxt = textFiles['../generated-docs/llms-full.txt'] ?? '';
|
package/dist/docs/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { components, guides } from '../../routes/registry.js';
|
|
2
2
|
import { buildComponentDoc } from './component-docs.js';
|
|
3
|
+
import { utilitiesDoc } from './utilities.js';
|
|
3
4
|
const rootUiIndexSources = import.meta.glob('../ui/index.ts', {
|
|
4
5
|
query: '?raw',
|
|
5
6
|
import: 'default',
|
|
@@ -14,6 +15,9 @@ function slugFromPath(path) {
|
|
|
14
15
|
return path.replace(/^\.\//, '').replace(/\.md$/, '');
|
|
15
16
|
}
|
|
16
17
|
const appendices = Object.fromEntries(Object.entries(markdownModules).map(([path, body]) => [slugFromPath(path), body]));
|
|
18
|
+
// The utilities guide has no component source to synthesize from — its body is
|
|
19
|
+
// parsed out of the `@utility` doc comments in src/lib/styles instead.
|
|
20
|
+
appendices.utilities = [appendices.utilities, utilitiesDoc].filter(Boolean).join('\n\n');
|
|
17
21
|
function titleFromSlug(slug) {
|
|
18
22
|
return slug
|
|
19
23
|
.split('-')
|
package/dist/docs/llms-txt.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { docs, hasDoc, listEntries } from './index.js';
|
|
2
|
+
import { buildUtilitiesIndex } from './css-utilities.js';
|
|
3
|
+
import { utilities } from './utilities.js';
|
|
2
4
|
import { modes, defaultMode, surfaces, defaultSurface, corners, defaultCorners, fonts, defaultFont, motions, defaultMotion } from '../ui/provider/index.js';
|
|
3
5
|
const H1 = '# era-ui';
|
|
4
6
|
const MISSION = '> A Svelte 5 + Bits UI component library. Every surface — heights, paddings, radii, gaps — derives from a single spacing atom, so density is a one-attribute override at any scope.';
|
|
@@ -49,6 +51,17 @@ function orientation(origin) {
|
|
|
49
51
|
'Each page is generated from the component source, so props/variants/defaults stay in sync with the code automatically.'
|
|
50
52
|
].join('\n');
|
|
51
53
|
}
|
|
54
|
+
function utilitiesSection(origin) {
|
|
55
|
+
return [
|
|
56
|
+
'## CSS utilities',
|
|
57
|
+
'',
|
|
58
|
+
'Classes, not components — they need no import beyond the stylesheet. Reach for one of these before hand-rolling the same styling or bending a component into the role.',
|
|
59
|
+
'',
|
|
60
|
+
...buildUtilitiesIndex(utilities),
|
|
61
|
+
'',
|
|
62
|
+
`Full reference (what each emits, when to use it): \`${origin}/utilities.md\`. Machine-readable list: \`${origin}/utilities.json\`.`
|
|
63
|
+
].join('\n');
|
|
64
|
+
}
|
|
52
65
|
export function buildLlmsTxt(origin = '') {
|
|
53
66
|
const entries = listEntries();
|
|
54
67
|
const lines = [H1, '', MISSION, '', orientation(origin), ''];
|
|
@@ -56,6 +69,7 @@ export function buildLlmsTxt(origin = '') {
|
|
|
56
69
|
lines.push('## Components', '');
|
|
57
70
|
lines.push(entries.map((e) => e.slug).join(', '), '');
|
|
58
71
|
}
|
|
72
|
+
lines.push(utilitiesSection(origin), '');
|
|
59
73
|
return lines.join('\n');
|
|
60
74
|
}
|
|
61
75
|
// Module-init-cached: the concatenated reference has no request-dependent
|
|
@@ -73,7 +87,9 @@ export function buildLlmsFullTxt() {
|
|
|
73
87
|
'',
|
|
74
88
|
orientation(''),
|
|
75
89
|
'',
|
|
76
|
-
'
|
|
90
|
+
utilitiesSection(''),
|
|
91
|
+
'',
|
|
92
|
+
'<!-- Full component documentation, concatenated. The utilities page below carries the CSS each utility emits. -->',
|
|
77
93
|
''
|
|
78
94
|
];
|
|
79
95
|
for (const e of entries) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
## Notes
|
|
2
|
+
|
|
3
|
+
- For a region that should scroll but show no scrollbar at all — an overflowing
|
|
4
|
+
tab strip, a chip rail, a small pane handle — you don't need ScrollArea: put
|
|
5
|
+
the `scrollbar-none` CSS utility on the overflowing element. See
|
|
6
|
+
[Utilities](/utilities.md). ScrollArea is for the case where the bar itself is
|
|
7
|
+
part of the design.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Runtime side of the CSS-utility docs: globs the shipped stylesheets and parses
|
|
3
|
+
* their `@utility` blocks. The node build script (scripts/build-llm-docs.ts)
|
|
4
|
+
* feeds the same parser from the filesystem, so the site and the generated
|
|
5
|
+
* markdown can never disagree about what era exposes.
|
|
6
|
+
*/
|
|
7
|
+
import { buildUtilitiesDoc, parseUtilities } from './css-utilities.js';
|
|
8
|
+
const cssSources = import.meta.glob('../styles/**/*.css', {
|
|
9
|
+
query: '?raw',
|
|
10
|
+
import: 'default',
|
|
11
|
+
eager: true
|
|
12
|
+
});
|
|
13
|
+
const sources = Object.fromEntries(Object.entries(cssSources).map(([path, body]) => [path.replace(/^\.\.\/styles\//, ''), body]));
|
|
14
|
+
export const utilities = parseUtilities(sources);
|
|
15
|
+
export const utilitiesDoc = buildUtilitiesDoc(utilities);
|