@f-ewald/components 1.2.0 → 1.3.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 +15 -2
- package/custom-elements.json +1020 -76
- package/dist/action-bar.d.ts +24 -0
- package/dist/action-bar.d.ts.map +1 -0
- package/dist/action-bar.js +67 -0
- package/dist/action-bar.js.map +1 -0
- package/dist/app-shell.d.ts +86 -0
- package/dist/app-shell.d.ts.map +1 -0
- package/dist/app-shell.js +460 -0
- package/dist/app-shell.js.map +1 -0
- package/dist/app-sidebar.d.ts +44 -0
- package/dist/app-sidebar.d.ts.map +1 -0
- package/dist/app-sidebar.js +211 -0
- package/dist/app-sidebar.js.map +1 -0
- package/dist/form-actions.d.ts +27 -0
- package/dist/form-actions.d.ts.map +1 -0
- package/dist/form-actions.js +65 -0
- package/dist/form-actions.js.map +1 -0
- package/dist/icons.d.ts +1 -0
- package/dist/icons.d.ts.map +1 -1
- package/dist/icons.js +1 -0
- package/dist/icons.js.map +1 -1
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp-server.js +82 -5
- package/dist/mcp-server.js.map +1 -1
- package/dist/page-header.d.ts +27 -0
- package/dist/page-header.d.ts.map +1 -0
- package/dist/page-header.js +106 -0
- package/dist/page-header.js.map +1 -0
- package/dist/pagination-nav.d.ts +34 -0
- package/dist/pagination-nav.d.ts.map +1 -0
- package/dist/pagination-nav.js +144 -0
- package/dist/pagination-nav.js.map +1 -0
- package/dist/popover-panel.js +1 -1
- package/dist/popover-panel.js.map +1 -1
- package/dist/timeline-container.d.ts +22 -0
- package/dist/timeline-container.d.ts.map +1 -0
- package/dist/timeline-container.js +42 -0
- package/dist/timeline-container.js.map +1 -0
- package/dist/timeline-entry.d.ts +31 -0
- package/dist/timeline-entry.d.ts.map +1 -0
- package/dist/timeline-entry.js +170 -0
- package/dist/timeline-entry.js.map +1 -0
- package/docs/action-bar.md +41 -0
- package/docs/app-shell.md +85 -0
- package/docs/app-sidebar.md +69 -0
- package/docs/design-language.md +74 -1
- package/docs/form-actions.md +43 -0
- package/docs/layouts/detail-only.md +52 -0
- package/docs/layouts/form-page.md +52 -0
- package/docs/layouts/list-detail.md +52 -0
- package/docs/layouts/list-only.md +50 -0
- package/docs/layouts/record-detail.md +88 -0
- package/docs/mcp-evaluation.md +21 -0
- package/docs/page-header.md +47 -0
- package/docs/pagination-nav.md +54 -0
- package/docs/timeline-container.md +44 -0
- package/docs/timeline-entry.md +52 -0
- package/llms.txt +229 -0
- package/package.json +1 -1
package/docs/design-language.md
CHANGED
|
@@ -134,13 +134,86 @@ literal values are migrated onto them.
|
|
|
134
134
|
|
|
135
135
|
- Metadata-only tags may omit standalone playground sections when their full
|
|
136
136
|
behavior is demonstrated through a parent:
|
|
137
|
-
`calendar-entry`, `gallery-item`,
|
|
137
|
+
`calendar-entry`, `gallery-item`, `gallery-item-variant`, `kanban-card`,
|
|
138
|
+
`kanban-column`, and `timeline-entry` (shown through `timeline-container`).
|
|
138
139
|
- Styleless inline formatters may omit empty `static styles`/token imports:
|
|
139
140
|
`distance-value`, `live-timer`, `relative-time`, and `roman-numeral`.
|
|
140
141
|
- Domain visuals may deliberately diverge in geometry and data color, but their
|
|
141
142
|
surrounding typography, focus, motion, and accessibility still follow this
|
|
142
143
|
contract.
|
|
143
144
|
|
|
145
|
+
## Layout and page templates
|
|
146
|
+
|
|
147
|
+
The layout components compose into dashboard pages: `app-shell` is the grid
|
|
148
|
+
backbone, and `app-sidebar`, `action-bar`, `page-header`, `pagination-nav`, and
|
|
149
|
+
`form-actions` fill its slots and content.
|
|
150
|
+
|
|
151
|
+
### Shell grid and metrics
|
|
152
|
+
|
|
153
|
+
- `app-shell` owns a CSS grid with `sidebar`, `topbar`, `main` (default slot),
|
|
154
|
+
`detail`, and `footer` areas plus the responsive behavior. It is the only
|
|
155
|
+
component that coordinates the sidebar rail and the detail overlay.
|
|
156
|
+
- Layout dimensions are documented literal rems, not `--ui-*` tokens (widths and
|
|
157
|
+
spacing are constants in this system). A few are exposed as overridable
|
|
158
|
+
`--component-*` custom properties for per-instance tuning, mirroring
|
|
159
|
+
`--component-layer-z`:
|
|
160
|
+
- sidebar expanded `--component-sidebar-width` = `16rem`;
|
|
161
|
+
- sidebar rail `--component-sidebar-rail-width` = `3.5rem`;
|
|
162
|
+
- top bar `--component-topbar-height` = `3rem`;
|
|
163
|
+
- detail column reuses the `20rem` / `25rem` panel widths (the `detail-width`
|
|
164
|
+
`compact` / `comfortable` attribute);
|
|
165
|
+
- main content is white (`--component-main-background`, default `--ui-surface`)
|
|
166
|
+
and fluid; constrain reading or form pages with a `max-width` on your own
|
|
167
|
+
wrapper (roughly `40rem`–`48rem`).
|
|
168
|
+
- The shared `48rem` breakpoint remains the only responsive breakpoint. At or
|
|
169
|
+
below it the sidebar becomes an off-canvas drawer and the detail an overlay,
|
|
170
|
+
both raised with `--component-layer-z` and dismissed by a scrim or Escape.
|
|
171
|
+
|
|
172
|
+
### Sidebar
|
|
173
|
+
|
|
174
|
+
- Collapsing is a two-mode design: on desktop the sidebar condenses to an icon
|
|
175
|
+
rail (labels hide, icons stay) via `app-shell`'s `sidebar-collapsed`, which
|
|
176
|
+
propagates `collapsed` to the slotted `app-sidebar`; below `48rem` it is a
|
|
177
|
+
drawer with labels shown.
|
|
178
|
+
- The built-in top-bar toggle and the `[` keyboard shortcut (ignored while a
|
|
179
|
+
text field is focused or a modifier is held) both drive `sidebar-collapsed`.
|
|
180
|
+
The shortcut is surfaced in the toggle's hover/focus tooltip, not as permanent
|
|
181
|
+
chrome — reveal shortcuts on interaction rather than displaying them inline.
|
|
182
|
+
- Nav items are the consumer's `<a>`/`<button>` elements with an icon and a
|
|
183
|
+
label; mark the active one with `aria-current="page"`. Each label reads the
|
|
184
|
+
inherited `--app-sidebar-label` custom property so rail mode can hide it while
|
|
185
|
+
its `aria-label` keeps the accessible name.
|
|
186
|
+
- The `header` slot holds the brand: a small logo plus a name. Wrap the name in
|
|
187
|
+
a `--app-sidebar-label` element so rail mode shows only the logo, centered in
|
|
188
|
+
line with the nav icons.
|
|
189
|
+
|
|
190
|
+
### Forms and actions
|
|
191
|
+
|
|
192
|
+
- The action row above a list uses `action-bar` (`start` = search/filters,
|
|
193
|
+
`end` = record actions). Pagination below a list uses `pagination-nav`, which
|
|
194
|
+
is controlled: the consumer owns the data and moves the page on `page-change`.
|
|
195
|
+
- Form button order is fixed: the primary/submit button is rightmost, the
|
|
196
|
+
secondary/cancel button is immediately to its left, and any tertiary or
|
|
197
|
+
destructive action is pinned to the far left. Use `form-actions`, which
|
|
198
|
+
enforces this regardless of source order — never hand-place form buttons in a
|
|
199
|
+
different order.
|
|
200
|
+
- Page and record actions live in the `page-header` actions slot, not a footer
|
|
201
|
+
bar: the primary action is the prominent, rightmost button, and destructive or
|
|
202
|
+
rare actions are de-emphasized (a secondary button or an overflow menu, gated
|
|
203
|
+
by a `confirm-dialog`). Never duplicate the primary action in both a header and
|
|
204
|
+
a footer.
|
|
205
|
+
- Reserve `form-actions` for the bottom of an actual editable form. Read (detail)
|
|
206
|
+
and edit (form) are distinct modes: a read view acts through its header and has
|
|
207
|
+
no Save/Cancel footer; entering edit shows the form with a `form-actions` bar.
|
|
208
|
+
- Fence clearly distinct sections of a detail or form page with `frame-box`.
|
|
209
|
+
|
|
210
|
+
### Templates
|
|
211
|
+
|
|
212
|
+
The recipes under `docs/layouts/` compose these into four pages — list-only,
|
|
213
|
+
list + detail, detail-only, and form — and the MCP `list_layouts` /
|
|
214
|
+
`get_layout` tools serve them. Add a page by adding a `docs/layouts/<name>.md`
|
|
215
|
+
recipe and, ideally, a matching full-page demo under `demo/layouts/`.
|
|
216
|
+
|
|
144
217
|
## Playground and documentation
|
|
145
218
|
|
|
146
219
|
- Visual/interactive nav links and complete sections are alphabetical and have
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# `<form-actions>`
|
|
2
|
+
|
|
3
|
+
Form footer button bar with a fixed action order for internal apps: the
|
|
4
|
+
primary (submit) button is always rightmost, the secondary (cancel) button
|
|
5
|
+
sits to its immediate left, and an optional tertiary/destructive action is
|
|
6
|
+
pinned to the far left. The order is enforced by the component regardless of
|
|
7
|
+
the source order the buttons are authored in, so every form in a product
|
|
8
|
+
reads the same way.
|
|
9
|
+
|
|
10
|
+
Purely presentational: it lays out whatever controls (usually `ui-button`)
|
|
11
|
+
are slotted and never intercepts their clicks, `type="submit"`, or events.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
import "@f-ewald/components/form-actions.js";
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<form-actions>
|
|
23
|
+
<ui-button slot="start" variant="danger">Delete</ui-button>
|
|
24
|
+
<ui-button slot="secondary" variant="secondary">Cancel</ui-button>
|
|
25
|
+
<ui-button slot="primary" type="submit" variant="primary">Save</ui-button>
|
|
26
|
+
</form-actions>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Attributes / properties
|
|
30
|
+
|
|
31
|
+
_None._
|
|
32
|
+
|
|
33
|
+
## Events
|
|
34
|
+
|
|
35
|
+
_None._
|
|
36
|
+
|
|
37
|
+
## Slots
|
|
38
|
+
|
|
39
|
+
_None._
|
|
40
|
+
|
|
41
|
+
## CSS custom properties
|
|
42
|
+
|
|
43
|
+
_None._
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Detail-only page
|
|
2
|
+
|
|
3
|
+
A single record's read view: navigation, a titled header carrying the record
|
|
4
|
+
actions, and the record body grouped into framed sections.
|
|
5
|
+
|
|
6
|
+
**When to use:** viewing one record on its own route (linked to from a list).
|
|
7
|
+
Read and edit are distinct modes — keep this page read-only and let its primary
|
|
8
|
+
action navigate to the form page for editing.
|
|
9
|
+
|
|
10
|
+
## Shell slots
|
|
11
|
+
|
|
12
|
+
- `sidebar` → `app-sidebar`.
|
|
13
|
+
- `topbar` → `page-header` with a linked `breadcrumb` and the record `actions`.
|
|
14
|
+
- default (`main`) → the record body; group distinct sections with `frame-box`.
|
|
15
|
+
|
|
16
|
+
## Markup
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<app-shell style="height: 100vh">
|
|
20
|
+
<app-sidebar slot="sidebar"><!-- nav items --></app-sidebar>
|
|
21
|
+
|
|
22
|
+
<page-header slot="topbar" heading="Ada Lovelace">
|
|
23
|
+
<nav slot="breadcrumb" aria-label="Breadcrumb"><a href="/members">Members</a> / Ada Lovelace</nav>
|
|
24
|
+
<!-- Record actions: primary rightmost, destructive de-emphasized. -->
|
|
25
|
+
<ui-button slot="actions" variant="secondary">Deactivate</ui-button>
|
|
26
|
+
<ui-button slot="actions" variant="primary">Edit</ui-button>
|
|
27
|
+
</page-header>
|
|
28
|
+
|
|
29
|
+
<div style="max-width: 48rem;">
|
|
30
|
+
<frame-box label="Profile"><!-- fields --></frame-box>
|
|
31
|
+
<frame-box label="Access"><!-- fields --></frame-box>
|
|
32
|
+
</div>
|
|
33
|
+
</app-shell>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Notes
|
|
37
|
+
|
|
38
|
+
- Put record actions in the header, not a footer bar. The primary action (Edit)
|
|
39
|
+
is the rightmost, prominent button; keep destructive actions (Deactivate,
|
|
40
|
+
Delete) de-emphasized — a secondary button, or an overflow menu — and gate
|
|
41
|
+
them with a `confirm-dialog`.
|
|
42
|
+
- Do **not** add a `form-actions` footer here. `form-actions` (Save/Cancel) is
|
|
43
|
+
for the bottom of an editable form; a read view has nothing to save. Reserve
|
|
44
|
+
it for the form page — which is where Edit navigates.
|
|
45
|
+
- Back navigation comes from the breadcrumb link, so there is no separate Close
|
|
46
|
+
button.
|
|
47
|
+
- Constrain the record body with a `max-width` (about `40rem`–`48rem`) and fence
|
|
48
|
+
distinct field groups with `frame-box`.
|
|
49
|
+
|
|
50
|
+
**Live demo:** `demo/layouts/detail-only.html`
|
|
51
|
+
|
|
52
|
+
**Components:** app-shell, app-sidebar, page-header, frame-box, ui-button
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Form page
|
|
2
|
+
|
|
3
|
+
A create/edit form: navigation, a titled header, a single form grouped into
|
|
4
|
+
framed sections, and a `form-actions` bar with the fixed button order.
|
|
5
|
+
|
|
6
|
+
**When to use:** creating or editing one record. Keep the form in a single
|
|
7
|
+
column so the eye tracks one field to the next.
|
|
8
|
+
|
|
9
|
+
## Shell slots
|
|
10
|
+
|
|
11
|
+
- `sidebar` → `app-sidebar`.
|
|
12
|
+
- `topbar` → `page-header` with a `breadcrumb`.
|
|
13
|
+
- default (`main`) → one `<form>`; group fields with `frame-box` and end with
|
|
14
|
+
`form-actions`.
|
|
15
|
+
|
|
16
|
+
## Markup
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<app-shell style="height: 100vh">
|
|
20
|
+
<app-sidebar slot="sidebar"><!-- nav items --></app-sidebar>
|
|
21
|
+
|
|
22
|
+
<page-header slot="topbar" heading="Invite member">
|
|
23
|
+
<nav slot="breadcrumb" aria-label="Breadcrumb">Members / Invite</nav>
|
|
24
|
+
</page-header>
|
|
25
|
+
|
|
26
|
+
<form style="max-width: 40rem;">
|
|
27
|
+
<frame-box label="Details"><!-- name, email fields --></frame-box>
|
|
28
|
+
<frame-box label="Role"><!-- role field --></frame-box>
|
|
29
|
+
|
|
30
|
+
<form-actions>
|
|
31
|
+
<ui-button slot="secondary" variant="secondary">Cancel</ui-button>
|
|
32
|
+
<ui-button slot="primary" type="submit" variant="primary">Send invite</ui-button>
|
|
33
|
+
</form-actions>
|
|
34
|
+
</form>
|
|
35
|
+
</app-shell>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Notes
|
|
39
|
+
|
|
40
|
+
- Button order is fixed and enforced by `form-actions`: primary/submit rightmost,
|
|
41
|
+
secondary/cancel to its left, any destructive action pinned far left. Never
|
|
42
|
+
hand-place form buttons in a different order.
|
|
43
|
+
- Constrain the form with a `max-width` (about `40rem`); a full-width form on a
|
|
44
|
+
wide dashboard is hard to scan.
|
|
45
|
+
- Group related fields with `frame-box`; keep one field per row in a single
|
|
46
|
+
column.
|
|
47
|
+
- `ui-button type="submit"` associates with the ancestor `<form>` even from
|
|
48
|
+
inside `form-actions`.
|
|
49
|
+
|
|
50
|
+
**Live demo:** `demo/layouts/form-page.html`
|
|
51
|
+
|
|
52
|
+
**Components:** app-shell, app-sidebar, page-header, frame-box, form-actions, ui-button
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# List + detail page
|
|
2
|
+
|
|
3
|
+
A master-detail dashboard: the list-only page plus a right-hand detail column.
|
|
4
|
+
Selecting a row shows its detail beside the list on desktop and as an overlay on
|
|
5
|
+
narrow screens.
|
|
6
|
+
|
|
7
|
+
**When to use:** triaging or editing records where keeping the list visible
|
|
8
|
+
while inspecting one item speeds up the workflow (inbox-style screens, admin
|
|
9
|
+
record management).
|
|
10
|
+
|
|
11
|
+
## Shell slots
|
|
12
|
+
|
|
13
|
+
- `sidebar` → `app-sidebar`.
|
|
14
|
+
- `topbar` → `page-header`.
|
|
15
|
+
- default (`main`) → `action-bar` + `data-table`.
|
|
16
|
+
- `detail` → the selected record's detail; shown when `detail-open` is set.
|
|
17
|
+
- `footer` → `pagination-nav`.
|
|
18
|
+
|
|
19
|
+
## Markup
|
|
20
|
+
|
|
21
|
+
```html
|
|
22
|
+
<app-shell detail-width="comfortable" style="height: 100vh">
|
|
23
|
+
<app-sidebar slot="sidebar"><!-- nav items --></app-sidebar>
|
|
24
|
+
<page-header slot="topbar" heading="Members"></page-header>
|
|
25
|
+
|
|
26
|
+
<div>
|
|
27
|
+
<action-bar>
|
|
28
|
+
<autocomplete-input slot="start" placeholder="Search…"></autocomplete-input>
|
|
29
|
+
<ui-button slot="end" variant="primary">Create</ui-button>
|
|
30
|
+
</action-bar>
|
|
31
|
+
<data-table style="margin-top: 0.5rem; display: block;"></data-table>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<div slot="detail" style="padding: 1rem;"><!-- selected record --></div>
|
|
35
|
+
|
|
36
|
+
<pagination-nav slot="footer" current-page="1" total-pages="6"></pagination-nav>
|
|
37
|
+
</app-shell>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Notes
|
|
41
|
+
|
|
42
|
+
- Set `detail-open` on `app-shell` to reveal the detail column; clear it (or
|
|
43
|
+
listen for the `detail-close` event, fired when the mobile scrim/Escape
|
|
44
|
+
dismisses the overlay) to hide it.
|
|
45
|
+
- Wire a row selection (e.g. `data-table`'s `rowHref` + a hash listener, or your
|
|
46
|
+
router) to populate the detail slot and open it.
|
|
47
|
+
- `detail-width` is `compact` (20rem) or `comfortable` (25rem); below 48rem the
|
|
48
|
+
detail becomes an overlay regardless.
|
|
49
|
+
|
|
50
|
+
**Live demo:** `demo/layouts/list-detail.html`
|
|
51
|
+
|
|
52
|
+
**Components:** app-shell, app-sidebar, page-header, action-bar, data-table, pagination-nav
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# List-only page
|
|
2
|
+
|
|
3
|
+
A full-width list/table view: a collapsible sidebar, a top bar, an action row,
|
|
4
|
+
the list, and pagination. The default internal-dashboard "index" screen.
|
|
5
|
+
|
|
6
|
+
**When to use:** browsing or managing a collection of records where a row does
|
|
7
|
+
not need a side-by-side detail pane (rows link to their own detail page). If you
|
|
8
|
+
want an inline detail pane, use the list + detail template instead.
|
|
9
|
+
|
|
10
|
+
## Shell slots
|
|
11
|
+
|
|
12
|
+
- `sidebar` → `app-sidebar` with the app's navigation.
|
|
13
|
+
- `topbar` → `page-header` with the collection title (and optional actions).
|
|
14
|
+
- default (`main`) → an `action-bar` above a `data-table`.
|
|
15
|
+
- `footer` → `pagination-nav`.
|
|
16
|
+
|
|
17
|
+
## Markup
|
|
18
|
+
|
|
19
|
+
```html
|
|
20
|
+
<app-shell style="height: 100vh">
|
|
21
|
+
<app-sidebar slot="sidebar"><!-- nav items --></app-sidebar>
|
|
22
|
+
|
|
23
|
+
<page-header slot="topbar" heading="Members"></page-header>
|
|
24
|
+
|
|
25
|
+
<div>
|
|
26
|
+
<action-bar>
|
|
27
|
+
<autocomplete-input slot="start" placeholder="Search…"></autocomplete-input>
|
|
28
|
+
<ui-button slot="end" variant="secondary">Filter</ui-button>
|
|
29
|
+
<ui-button slot="end" variant="primary">Create</ui-button>
|
|
30
|
+
</action-bar>
|
|
31
|
+
<data-table style="margin-top: 1rem; display: block;"></data-table>
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<pagination-nav slot="footer" current-page="1" total-pages="6"></pagination-nav>
|
|
35
|
+
</app-shell>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Notes
|
|
39
|
+
|
|
40
|
+
- Give `app-shell` a height (e.g. `100vh`) so the sidebar and main can size and
|
|
41
|
+
scroll independently.
|
|
42
|
+
- `pagination-nav` is controlled: set `current-page`/`total-pages` and re-render
|
|
43
|
+
in response to its `page-change` event, the same way `data-table` leaves the
|
|
44
|
+
rows to you.
|
|
45
|
+
- Put search and filters in the action bar's `start` slot and record actions
|
|
46
|
+
(create, delete) in `end`.
|
|
47
|
+
|
|
48
|
+
**Live demo:** `demo/layouts/list-only.html`
|
|
49
|
+
|
|
50
|
+
**Components:** app-shell, app-sidebar, page-header, action-bar, data-table, pagination-nav
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Record + metadata rail page
|
|
2
|
+
|
|
3
|
+
A record's read view with the main content on the left and a persistent
|
|
4
|
+
metadata/detail rail on the right — the Jira/Linear issue layout (description +
|
|
5
|
+
status, assignee, labels, priority).
|
|
6
|
+
|
|
7
|
+
**When to use:** viewing one rich record whose metadata (status, owner, labels,
|
|
8
|
+
progress) should stay visible alongside the content, and where that metadata is
|
|
9
|
+
mostly a static display of components rather than a transient selection.
|
|
10
|
+
|
|
11
|
+
## Layout
|
|
12
|
+
|
|
13
|
+
The rail is a **two-column CSS grid inside the `main` slot**, not the shell's
|
|
14
|
+
`detail` slot:
|
|
15
|
+
|
|
16
|
+
- `sidebar` → `app-sidebar`.
|
|
17
|
+
- `topbar` → `page-header` with a linked `breadcrumb` and record `actions`
|
|
18
|
+
(primary Edit rightmost).
|
|
19
|
+
- default (`main`) → a grid: the body (description, activity) on the left, an
|
|
20
|
+
`<aside>` metadata rail on the right. It collapses to one column at the shared
|
|
21
|
+
48rem breakpoint so the metadata **stacks below** the content on narrow
|
|
22
|
+
screens instead of hiding.
|
|
23
|
+
|
|
24
|
+
The metadata rail is composed from existing components: `frame-box` to group,
|
|
25
|
+
`status-pill` for status/priority/labels, `user-avatar` for people, and
|
|
26
|
+
`stat-meter` for progress. Activity uses `chat-message`.
|
|
27
|
+
|
|
28
|
+
## Markup
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<app-shell style="height: 100vh">
|
|
32
|
+
<app-sidebar slot="sidebar"><!-- nav items --></app-sidebar>
|
|
33
|
+
|
|
34
|
+
<page-header slot="topbar" heading="Fix flaky login redirect">
|
|
35
|
+
<nav slot="breadcrumb" aria-label="Breadcrumb"><a href="/board">Board</a> / PROJ-142</nav>
|
|
36
|
+
<ui-button slot="actions" variant="primary">Edit</ui-button>
|
|
37
|
+
</page-header>
|
|
38
|
+
|
|
39
|
+
<div class="ticket">
|
|
40
|
+
<div class="ticket-body">
|
|
41
|
+
<h3>Description</h3>
|
|
42
|
+
<p>…</p>
|
|
43
|
+
<frame-box label="Activity">
|
|
44
|
+
<chat-message role="user" author="Ada Lovelace">…</chat-message>
|
|
45
|
+
</frame-box>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<aside class="ticket-meta" aria-label="Issue details">
|
|
49
|
+
<frame-box label="Details">
|
|
50
|
+
<dl class="meta">
|
|
51
|
+
<div class="meta-row"><dt>Status</dt><dd><status-pill label="In Progress" color="info"></status-pill></dd></div>
|
|
52
|
+
<div class="meta-row"><dt>Assignee</dt><dd><user-avatar name="Ada Lovelace" size="24"></user-avatar> Ada Lovelace</dd></div>
|
|
53
|
+
<div class="meta-row"><dt>Priority</dt><dd><status-pill label="High" color="warning"></status-pill></dd></div>
|
|
54
|
+
</dl>
|
|
55
|
+
<stat-meter label="Progress" percent="60"></stat-meter>
|
|
56
|
+
</frame-box>
|
|
57
|
+
</aside>
|
|
58
|
+
</div>
|
|
59
|
+
</app-shell>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```css
|
|
63
|
+
.ticket {
|
|
64
|
+
display: grid;
|
|
65
|
+
grid-template-columns: minmax(0, 1fr) 18rem;
|
|
66
|
+
gap: 1.5rem;
|
|
67
|
+
align-items: start;
|
|
68
|
+
}
|
|
69
|
+
@media (max-width: 48rem) {
|
|
70
|
+
.ticket { grid-template-columns: minmax(0, 1fr); }
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Notes
|
|
75
|
+
|
|
76
|
+
- Use the two-column grid inside `main` (not the shell `detail` slot) when the
|
|
77
|
+
metadata is **persistent** and should stack on mobile. The shell's `detail`
|
|
78
|
+
slot is for a **transient master-detail selection** (list + selected item): on
|
|
79
|
+
mobile it becomes a dismissible overlay, which hides persistent record
|
|
80
|
+
metadata. Reach for it in the list + detail template, not here.
|
|
81
|
+
- Editable metadata is a drop-in swap: replace a `status-pill` with a
|
|
82
|
+
`form-select`, or the label pills with a `multi-select`.
|
|
83
|
+
- Keep the record actions in the header (primary Edit); there is no
|
|
84
|
+
`form-actions` footer on a read view.
|
|
85
|
+
|
|
86
|
+
**Live demo:** `demo/layouts/record-detail.html`
|
|
87
|
+
|
|
88
|
+
**Components:** app-shell, app-sidebar, page-header, frame-box, status-pill, user-avatar, stat-meter, chat-message
|
package/docs/mcp-evaluation.md
CHANGED
|
@@ -36,3 +36,24 @@ A thin stdio MCP server exposing two tools — `list_components` and
|
|
|
36
36
|
`get_component_docs(tag)` — backed by the same `custom-elements.json` this
|
|
37
37
|
package already generates and ships. No new data source was needed; the
|
|
38
38
|
server is just a different transport over data that already existed.
|
|
39
|
+
|
|
40
|
+
## Update (layout system): added `list_layouts` / `get_layout`
|
|
41
|
+
|
|
42
|
+
The dashboard layout system introduced cross-component knowledge — which
|
|
43
|
+
components compose into which page templates, the slot recipes, and the
|
|
44
|
+
button-placement rule — that no single component's generated doc captures. Two
|
|
45
|
+
thin tools, `list_layouts` and `get_layout(name)`, expose it.
|
|
46
|
+
|
|
47
|
+
This stays true to the server's founding principle: **no new data source.** The
|
|
48
|
+
tools read the authored `docs/layouts/*.md` recipes (which also ship in the npm
|
|
49
|
+
package and render on the docs site), not a bespoke structure invented for MCP.
|
|
50
|
+
The individual layout components still flow through `list_components` /
|
|
51
|
+
`get_component_docs` automatically.
|
|
52
|
+
|
|
53
|
+
**Is MCP the right fit here?** It's a reasonable, not slam-dunk, fit. The
|
|
54
|
+
justification is discoverability and targeted retrieval — an agent can list the
|
|
55
|
+
templates and pull just the one it needs, rather than loading everything. If the
|
|
56
|
+
guidance stayed tiny, `llms.txt` alone could carry it; the tools earn their keep
|
|
57
|
+
by making the "preferred layout" explicitly enumerable and by matching the
|
|
58
|
+
per-component tools consumers already call. The cost to watch is keeping the
|
|
59
|
+
recipes, the `demo/layouts/` demos, and the tool output in sync.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# `<page-header>`
|
|
2
|
+
|
|
3
|
+
Page title block for the top of a dashboard view: an optional breadcrumb
|
|
4
|
+
trail, the page heading, and a right-aligned cluster of page-level actions.
|
|
5
|
+
It only lays these out — the breadcrumb links and action buttons are entirely
|
|
6
|
+
the consumer's, so it stays framework- and router-agnostic.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```js
|
|
11
|
+
import "@f-ewald/components/page-header.js";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```html
|
|
17
|
+
<page-header heading="Team members">
|
|
18
|
+
<nav slot="breadcrumb" aria-label="Breadcrumb">Home / Settings / Members</nav>
|
|
19
|
+
<ui-button slot="actions" variant="primary">Invite</ui-button>
|
|
20
|
+
</page-header>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Attributes / properties
|
|
24
|
+
|
|
25
|
+
| Property | Attribute | Type | Default | Description |
|
|
26
|
+
| --- | --- | --- | --- | --- |
|
|
27
|
+
| `heading` | `heading` | `string` | `""` | The page heading text. |
|
|
28
|
+
|
|
29
|
+
## Events
|
|
30
|
+
|
|
31
|
+
_None._
|
|
32
|
+
|
|
33
|
+
## Slots
|
|
34
|
+
|
|
35
|
+
_None._
|
|
36
|
+
|
|
37
|
+
## CSS custom properties
|
|
38
|
+
|
|
39
|
+
| Custom property |
|
|
40
|
+
| --- |
|
|
41
|
+
| `--ui-font` |
|
|
42
|
+
| `--ui-font-size-lg` |
|
|
43
|
+
| `--ui-font-size-sm` |
|
|
44
|
+
| `--ui-font-weight-semibold` |
|
|
45
|
+
| `--ui-line-height-tight` |
|
|
46
|
+
| `--ui-text` |
|
|
47
|
+
| `--ui-text-muted` |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# `<pagination-nav>`
|
|
2
|
+
|
|
3
|
+
Minimal, controlled pager for list/table views: a previous/next control pair
|
|
4
|
+
around a "Page N of M" status. It owns no data — the consumer sets
|
|
5
|
+
`current-page` / `total-pages` and moves the page in response to the
|
|
6
|
+
`page-change` event (typically alongside its own data fetch), exactly like
|
|
7
|
+
`data-table` leaves the row data to the caller.
|
|
8
|
+
|
|
9
|
+
Previous is disabled on the first page and next on the last; neither fires an
|
|
10
|
+
event when there is nowhere to go.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
import "@f-ewald/components/pagination-nav.js";
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```html
|
|
21
|
+
<pagination-nav current-page="1" total-pages="5"></pagination-nav>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Attributes / properties
|
|
25
|
+
|
|
26
|
+
| Property | Attribute | Type | Default | Description |
|
|
27
|
+
| --- | --- | --- | --- | --- |
|
|
28
|
+
| `currentPage` | `current-page` | `number` | `1` | The 1-based current page. |
|
|
29
|
+
| `totalPages` | `total-pages` | `number` | `1` | The total number of pages (minimum 1). |
|
|
30
|
+
|
|
31
|
+
## Events
|
|
32
|
+
|
|
33
|
+
| Event | Description |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| `page-change` | The user picked a new page (`detail: { page }`). |
|
|
36
|
+
|
|
37
|
+
## Slots
|
|
38
|
+
|
|
39
|
+
_None._
|
|
40
|
+
|
|
41
|
+
## CSS custom properties
|
|
42
|
+
|
|
43
|
+
| Custom property |
|
|
44
|
+
| --- |
|
|
45
|
+
| `--ui-border` |
|
|
46
|
+
| `--ui-focus-ring` |
|
|
47
|
+
| `--ui-font` |
|
|
48
|
+
| `--ui-font-size-sm` |
|
|
49
|
+
| `--ui-font-weight-medium` |
|
|
50
|
+
| `--ui-line-height-tight` |
|
|
51
|
+
| `--ui-radius-sm` |
|
|
52
|
+
| `--ui-surface-muted` |
|
|
53
|
+
| `--ui-text` |
|
|
54
|
+
| `--ui-text-muted` |
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# `<timeline-container>`
|
|
2
|
+
|
|
3
|
+
Vertical timeline: a single connecting line runs down the left edge and each
|
|
4
|
+
slotted `timeline-entry` places a dot on it. This is a thin layout and
|
|
5
|
+
semantics wrapper — the entries draw the line segments and dots themselves,
|
|
6
|
+
so the container adds no gap between them (a gap would break the line).
|
|
7
|
+
Exposed to assistive technology as a list of events.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```js
|
|
12
|
+
import "@f-ewald/components/timeline-container.js";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<timeline-container>
|
|
19
|
+
<timeline-entry datetime="2026-07-23T09:00:00Z">
|
|
20
|
+
<span slot="headline">Deployment started</span>
|
|
21
|
+
Release v1.4.0 is rolling out.
|
|
22
|
+
</timeline-entry>
|
|
23
|
+
<timeline-entry datetime="2026-07-23T08:45:00Z">
|
|
24
|
+
<span slot="headline">Review approved</span>
|
|
25
|
+
<status-pill label="In Review" color="info"></status-pill>
|
|
26
|
+
</timeline-entry>
|
|
27
|
+
</timeline-container>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Attributes / properties
|
|
31
|
+
|
|
32
|
+
_None._
|
|
33
|
+
|
|
34
|
+
## Events
|
|
35
|
+
|
|
36
|
+
_None._
|
|
37
|
+
|
|
38
|
+
## Slots
|
|
39
|
+
|
|
40
|
+
_None._
|
|
41
|
+
|
|
42
|
+
## CSS custom properties
|
|
43
|
+
|
|
44
|
+
_None._
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# `<timeline-entry>`
|
|
2
|
+
|
|
3
|
+
One event on a `timeline-container`: a dot on the vertical line, an optional
|
|
4
|
+
headline, a relative timestamp ("3 hours ago"), and freely nested content.
|
|
5
|
+
The connecting line is drawn here — its segment above the dot is hidden on
|
|
6
|
+
the first entry and the segment below is hidden on the last, so the line caps
|
|
7
|
+
exactly at the first and last dots. Only meaningful inside a
|
|
8
|
+
`timeline-container`; demonstrated through it.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
import "@f-ewald/components/timeline-entry.js";
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```html
|
|
19
|
+
<timeline-entry datetime="2026-07-23T09:00:00Z">
|
|
20
|
+
<span slot="headline">Deployment started</span>
|
|
21
|
+
Release v1.4.0 is rolling out to production.
|
|
22
|
+
</timeline-entry>
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Attributes / properties
|
|
26
|
+
|
|
27
|
+
| Property | Attribute | Type | Default | Description |
|
|
28
|
+
| --- | --- | --- | --- | --- |
|
|
29
|
+
| `datetime` | `datetime` | `string | null` | `null` | ISO 8601 or SQLite datetime string, rendered as a relative time. |
|
|
30
|
+
|
|
31
|
+
## Events
|
|
32
|
+
|
|
33
|
+
_None._
|
|
34
|
+
|
|
35
|
+
## Slots
|
|
36
|
+
|
|
37
|
+
_None._
|
|
38
|
+
|
|
39
|
+
## CSS custom properties
|
|
40
|
+
|
|
41
|
+
| Custom property |
|
|
42
|
+
| --- |
|
|
43
|
+
| `--ui-border` |
|
|
44
|
+
| `--ui-font` |
|
|
45
|
+
| `--ui-font-size` |
|
|
46
|
+
| `--ui-font-size-sm` |
|
|
47
|
+
| `--ui-font-weight-semibold` |
|
|
48
|
+
| `--ui-line-height-normal` |
|
|
49
|
+
| `--ui-line-height-tight` |
|
|
50
|
+
| `--ui-primary` |
|
|
51
|
+
| `--ui-text` |
|
|
52
|
+
| `--ui-text-muted` |
|