@thespielplatz/tsp-tools-theme 0.2.1 → 0.3.1
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/CHANGELOG.md +144 -0
- package/README.md +148 -44
- package/assets/css/theme.css +86 -6
- package/components/TspBanner.vue +97 -0
- package/components/TspBrandTile.vue +46 -0
- package/components/TspCodeBlock.vue +68 -0
- package/components/TspContainer.vue +10 -2
- package/components/TspCopyButton.vue +40 -0
- package/components/TspCopyField.vue +102 -0
- package/components/TspFooterRow.vue +56 -0
- package/components/TspLanguageToggle.vue +75 -0
- package/components/TspNavBadge.vue +36 -0
- package/components/TspNavGroup.vue +58 -0
- package/components/TspNavItem.vue +68 -12
- package/components/TspNavLabel.vue +56 -0
- package/components/TspNavSection.vue +33 -0
- package/components/TspNavSubItem.vue +35 -0
- package/components/TspPageTabs.vue +46 -0
- package/components/TspReleaseNotes.vue +143 -0
- package/components/TspSectionCard.vue +52 -0
- package/components/TspSidebar.vue +96 -3
- package/components/TspSidebarFooter.vue +332 -37
- package/components/TspSiteFooter.vue +47 -16
- package/components/TspSiteHeader.vue +18 -2
- package/components/TspThemeToggle.vue +24 -5
- package/components/TspToolOf.vue +42 -0
- package/components/TspTopBar.vue +69 -0
- package/components/TspUserCard.vue +127 -0
- package/components/TspVersionBadge.vue +49 -0
- package/components/TspWordmark.vue +36 -4
- package/composables/useTspCopy.ts +72 -0
- package/composables/useTspNavDrawer.ts +14 -0
- package/composables/useTspReveal.ts +18 -0
- package/docs/api.md +394 -0
- package/docs/design-system.md +297 -0
- package/nuxt.config.ts +11 -0
- package/package.json +24 -13
- package/page-meta.d.ts +11 -0
- package/plugins/01.tspColorMode.ts +12 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Generated from conventional commits by [changelogen](https://github.com/unjs/changelogen)
|
|
4
|
+
(`npm run bump-version`). Releases before `v0.3.0` were reconstructed from history — the
|
|
5
|
+
changelog ADR 005 asks for had never been generated.
|
|
6
|
+
|
|
7
|
+
Consumers pin a caret range and do not auto-update, so a release only reaches a service when
|
|
8
|
+
that service's own spec says so. This package has no users of its own — every entry below is a
|
|
9
|
+
change to eight apps at once, at the moment each of them chooses to take it.
|
|
10
|
+
|
|
11
|
+
## v0.3.1
|
|
12
|
+
|
|
13
|
+
[compare changes](https://github.com/inf0matics/tsp-tools-theme/compare/v0.3.0...v0.3.1)
|
|
14
|
+
|
|
15
|
+
`0.3.1` exists to prove the automated release path end to end: `npm run bump-version` cuts it,
|
|
16
|
+
a pushed tag publishes it over OIDC with no npm token anywhere. It contains no behaviour change —
|
|
17
|
+
no consumer needs it.
|
|
18
|
+
|
|
19
|
+
Provenance was attempted and abandoned: npm signs provenance only for a **public** source
|
|
20
|
+
repository, and this one is private (`422 … Unsupported GitHub Actions source repository
|
|
21
|
+
visibility: "private"`).
|
|
22
|
+
|
|
23
|
+
### 🩹 Fixes
|
|
24
|
+
|
|
25
|
+
- **ci:** Give the provenance check 10 minutes, not 200 seconds ([801182d](https://github.com/inf0matics/tsp-tools-theme/commit/801182d))
|
|
26
|
+
- **ci:** Drop provenance — it requires a public repository
|
|
27
|
+
- **ci:** Keep package-lock.json in step with the version on release
|
|
28
|
+
|
|
29
|
+
### 🏡 Chore
|
|
30
|
+
|
|
31
|
+
- Changelogen must not cut the GitHub release ([c564e71](https://github.com/inf0matics/tsp-tools-theme/commit/c564e71))
|
|
32
|
+
|
|
33
|
+
### 🤖 CI
|
|
34
|
+
|
|
35
|
+
- Ask for provenance explicitly, and verify it landed ([e96945a](https://github.com/inf0matics/tsp-tools-theme/commit/e96945a))
|
|
36
|
+
|
|
37
|
+
### ❤️ Contributors
|
|
38
|
+
|
|
39
|
+
- Inf0matics <philipp.horwath@gmail.com>
|
|
40
|
+
|
|
41
|
+
## v0.3.0 — 2026-09-22
|
|
42
|
+
|
|
43
|
+
[compare changes](https://github.com/inf0matics/tsp-tools-theme/compare/v0.2.1...main)
|
|
44
|
+
|
|
45
|
+
### 🚀 Enhancements
|
|
46
|
+
|
|
47
|
+
- Quiet shell, language toggle and shared footer row ([347776a](https://github.com/inf0matics/tsp-tools-theme/commit/347776a))
|
|
48
|
+
- TspBrandTile — the frame around a service's mark ([62d6f3a](https://github.com/inf0matics/tsp-tools-theme/commit/62d6f3a))
|
|
49
|
+
- Sidebar footer rows, source variants, and the wordmark fix ([ed4be66](https://github.com/inf0matics/tsp-tools-theme/commit/ed4be66))
|
|
50
|
+
- Nav groups and section headers, white nav at rest ([a15a97c](https://github.com/inf0matics/tsp-tools-theme/commit/a15a97c))
|
|
51
|
+
- Icons on the user row and on nav children ([97011c5](https://github.com/inf0matics/tsp-tools-theme/commit/97011c5))
|
|
52
|
+
- TspUserCard — the user page, rebuilt from stash ([4cd1a48](https://github.com/inf0matics/tsp-tools-theme/commit/4cd1a48))
|
|
53
|
+
- Move the language row below the divider ([62cde28](https://github.com/inf0matics/tsp-tools-theme/commit/62cde28))
|
|
54
|
+
- One meta row — theme, github, support, tip jar, version ([b1fe4fe](https://github.com/inf0matics/tsp-tools-theme/commit/b1fe4fe))
|
|
55
|
+
- Preferences row — theme label left, language toggle right ([4f0d4d8](https://github.com/inf0matics/tsp-tools-theme/commit/4f0d4d8))
|
|
56
|
+
- Nav badges, version tag icon, pre-addressed support mail ([c35e734](https://github.com/inf0matics/tsp-tools-theme/commit/c35e734))
|
|
57
|
+
- Truncated nav labels reveal their full text on hover ([17471ab](https://github.com/inf0matics/tsp-tools-theme/commit/17471ab))
|
|
58
|
+
- Page structure — logged-in vs logged-out, and external nav rows ([b777621](https://github.com/inf0matics/tsp-tools-theme/commit/b777621))
|
|
59
|
+
- The sidebar works on mobile — drawer, top bar, burger ([323ce31](https://github.com/inf0matics/tsp-tools-theme/commit/323ce31))
|
|
60
|
+
- Harvest the old mockups; quiet the sidebar scrollbar ([0442ba3](https://github.com/inf0matics/tsp-tools-theme/commit/0442ba3))
|
|
61
|
+
- Landing page reshaped from stash; forms page; drop the prototypes ([a4db2bb](https://github.com/inf0matics/tsp-tools-theme/commit/a4db2bb))
|
|
62
|
+
- MCP settings page, generalised from trips ([1046d24](https://github.com/inf0matics/tsp-tools-theme/commit/1046d24))
|
|
63
|
+
- MCP settings in the scoped demo; card and code sections on /content ([3085716](https://github.com/inf0matics/tsp-tools-theme/commit/3085716))
|
|
64
|
+
- Release surfaces — banner, notes overview, unread badge, modal ([4bbe783](https://github.com/inf0matics/tsp-tools-theme/commit/4bbe783))
|
|
65
|
+
- Widen the app measure to 1152px ([b0e189d](https://github.com/inf0matics/tsp-tools-theme/commit/b0e189d))
|
|
66
|
+
|
|
67
|
+
### 🩹 Fixes
|
|
68
|
+
|
|
69
|
+
- Honour an explicit --port in scripts/dev.mjs ([fab8186](https://github.com/inf0matics/tsp-tools-theme/commit/fab8186))
|
|
70
|
+
- Centre the mark again; add checkboxes, radios and a textarea ([4da3df5](https://github.com/inf0matics/tsp-tools-theme/commit/4da3df5))
|
|
71
|
+
- Give the surfaces three rungs, so a card is not the sidebar ([a53ce8e](https://github.com/inf0matics/tsp-tools-theme/commit/a53ce8e))
|
|
72
|
+
- Pin the shell so it cannot scroll away; gate both apps for icons ([a44bb52](https://github.com/inf0matics/tsp-tools-theme/commit/a44bb52))
|
|
73
|
+
- **review:** Close the review findings — two guards did not actually guard ([1d621ed](https://github.com/inf0matics/tsp-tools-theme/commit/1d621ed))
|
|
74
|
+
- Reserve the scrollbar gutter so the content column stops shifting ([01de755](https://github.com/inf0matics/tsp-tools-theme/commit/01de755))
|
|
75
|
+
- TspUserCard no longer caps the page width ([cc11f04](https://github.com/inf0matics/tsp-tools-theme/commit/cc11f04))
|
|
76
|
+
- The MCP settings page no longer caps its own width ([65b4cbd](https://github.com/inf0matics/tsp-tools-theme/commit/65b4cbd))
|
|
77
|
+
|
|
78
|
+
### 💅 Refactors
|
|
79
|
+
|
|
80
|
+
- Consolidate duplicated behaviour; docs move into the package ([b34e898](https://github.com/inf0matics/tsp-tools-theme/commit/b34e898))
|
|
81
|
+
|
|
82
|
+
### 📖 Documentation
|
|
83
|
+
|
|
84
|
+
- Correct the @nuxt/icon pin notes for 2.5.1 ([43e68b7](https://github.com/inf0matics/tsp-tools-theme/commit/43e68b7))
|
|
85
|
+
- Reconcile README with the shipped theme; share TspToolOf ([4f72351](https://github.com/inf0matics/tsp-tools-theme/commit/4f72351))
|
|
86
|
+
- Generate the API reference, and document 71 undocumented props ([30a77db](https://github.com/inf0matics/tsp-tools-theme/commit/30a77db))
|
|
87
|
+
|
|
88
|
+
### 🏡 Chore
|
|
89
|
+
|
|
90
|
+
- Check in the working agreement and dev launch config ([426dba2](https://github.com/inf0matics/tsp-tools-theme/commit/426dba2))
|
|
91
|
+
- Add scripts/dev.mjs so dev servers pick a free port ([996d0ef](https://github.com/inf0matics/tsp-tools-theme/commit/996d0ef))
|
|
92
|
+
- **release:** V0.3.0 ([0a5c793](https://github.com/inf0matics/tsp-tools-theme/commit/0a5c793))
|
|
93
|
+
- **playground:** Nav prototype — sub-items and section headers ([7c8938b](https://github.com/inf0matics/tsp-tools-theme/commit/7c8938b))
|
|
94
|
+
|
|
95
|
+
### ✅ Tests
|
|
96
|
+
|
|
97
|
+
- E2e harness covering both consumption modes ([bff7d0a](https://github.com/inf0matics/tsp-tools-theme/commit/bff7d0a))
|
|
98
|
+
|
|
99
|
+
## v0.2.1 — 2026-07-28
|
|
100
|
+
|
|
101
|
+
[compare changes](https://github.com/inf0matics/tsp-tools-theme/compare/v0.2.0...v0.2.1)
|
|
102
|
+
|
|
103
|
+
### 🩹 Fixes
|
|
104
|
+
|
|
105
|
+
- Cap @nuxt/icon at ~2.3.1 and gate on icon rendering ([9e4d67d](https://github.com/inf0matics/tsp-tools-theme/commit/9e4d67d))
|
|
106
|
+
|
|
107
|
+
### 🏡 Chore
|
|
108
|
+
|
|
109
|
+
- **release:** V0.2.1 ([1d0925b](https://github.com/inf0matics/tsp-tools-theme/commit/1d0925b))
|
|
110
|
+
|
|
111
|
+
## v0.2.0 — 2026-07-28
|
|
112
|
+
|
|
113
|
+
[compare changes](https://github.com/inf0matics/tsp-tools-theme/compare/v0.1.3...v0.2.0)
|
|
114
|
+
|
|
115
|
+
### 🚀 Enhancements
|
|
116
|
+
|
|
117
|
+
- Site shell, container widths, localisable theme toggle ([a16c5ff](https://github.com/inf0matics/tsp-tools-theme/commit/a16c5ff))
|
|
118
|
+
|
|
119
|
+
### 🏡 Chore
|
|
120
|
+
|
|
121
|
+
- **release:** V0.2.0 ([0113c4c](https://github.com/inf0matics/tsp-tools-theme/commit/0113c4c))
|
|
122
|
+
|
|
123
|
+
## v0.1.3 — 2026-07-28
|
|
124
|
+
|
|
125
|
+
[compare changes](https://github.com/inf0matics/tsp-tools-theme/compare/v0.1.2...v0.1.3)
|
|
126
|
+
|
|
127
|
+
### 🩹 Fixes
|
|
128
|
+
|
|
129
|
+
- Keep colour-mode assignment type-safe under color-mode v4 ([4eb970b](https://github.com/inf0matics/tsp-tools-theme/commit/4eb970b))
|
|
130
|
+
|
|
131
|
+
### 🏡 Chore
|
|
132
|
+
|
|
133
|
+
- Stop the release script from double-bumping ([14fc551](https://github.com/inf0matics/tsp-tools-theme/commit/14fc551))
|
|
134
|
+
- **release:** V0.1.3 ([aa248ae](https://github.com/inf0matics/tsp-tools-theme/commit/aa248ae))
|
|
135
|
+
|
|
136
|
+
## v0.1.2 — 2026-07-28
|
|
137
|
+
|
|
138
|
+
[compare changes](https://github.com/inf0matics/tsp-tools-theme/compare/edd310cbdf8428b32bb3472164d99e0cbaf53d79...v0.1.2)
|
|
139
|
+
|
|
140
|
+
### 🏡 Chore
|
|
141
|
+
|
|
142
|
+
- Prepare package for npm publishing ([cde3422](https://github.com/inf0matics/tsp-tools-theme/commit/cde3422))
|
|
143
|
+
- **release:** V0.1.1 — exports ./package.json + CSS-ownership docs ([9629dd0](https://github.com/inf0matics/tsp-tools-theme/commit/9629dd0))
|
|
144
|
+
- **release:** V0.1.2 ([f18e614](https://github.com/inf0matics/tsp-tools-theme/commit/f18e614))
|
package/README.md
CHANGED
|
@@ -16,6 +16,16 @@ Two consumption modes:
|
|
|
16
16
|
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
+
> **The design system lives here:** [`docs/design-system.md`](docs/design-system.md) — tokens,
|
|
20
|
+
> components, rules and gates, read from `assets/css/theme.css` and `components/`. It is the
|
|
21
|
+
> source of truth for how tsp.tools services look; the planning project's
|
|
22
|
+
> `architecture/design-system.md` is a pointer to it.
|
|
23
|
+
>
|
|
24
|
+
> The **playground is the reference**, never a mockup. `npm run dev`.
|
|
25
|
+
>
|
|
26
|
+
> **Every prop, slot and emit:** [`docs/api.md`](docs/api.md) — generated from the components by
|
|
27
|
+
> `npm run docs:api` and verified in the gate, so it cannot go stale.
|
|
28
|
+
|
|
19
29
|
## Install
|
|
20
30
|
|
|
21
31
|
```bash
|
|
@@ -34,24 +44,29 @@ Peer expectations (provide them in the consuming app):
|
|
|
34
44
|
> (A deep relative path such as `extends: ['../..']` can fail to pick up the source dirs;
|
|
35
45
|
> the package name always works.)
|
|
36
46
|
|
|
37
|
-
### Why `@nuxt/icon` is
|
|
47
|
+
### Why `@nuxt/icon` is pinned at all
|
|
38
48
|
|
|
39
|
-
The layer declares `@nuxt/icon
|
|
40
|
-
|
|
49
|
+
The layer declares `@nuxt/icon` as a **direct dependency** on a tilde range — currently
|
|
50
|
+
`~2.5.1`. The range is deliberate, even though the original regression is fixed.
|
|
41
51
|
|
|
42
52
|
`@nuxt/icon` 2.4.1 changed its plugin to take `$fetch` from `useRequestFetch()` and then read
|
|
43
53
|
`.native` off it. During SSR that returns Nitro's request-scoped `event.$fetch`, which has no
|
|
44
|
-
`.native` property — so Iconify's fetch
|
|
45
|
-
|
|
46
|
-
|
|
54
|
+
`.native` property — so Iconify's fetch was set to `undefined` and **every server-side icon load
|
|
55
|
+
failed**, logging `[Icon] failed to load icon …` per request. The layer capped at `~2.3.1` to
|
|
56
|
+
escape it. 2.5.1 no longer has the bug, so the cap was lifted (2026-09-20).
|
|
57
|
+
|
|
58
|
+
It stays a **tilde** range rather than a caret: `@nuxt/ui` only requires `^2.3.x`, so any
|
|
59
|
+
re-resolve of the tree can move consumers across minors on its own, and that is precisely how
|
|
60
|
+
the broken version reached apps the first time. A tilde means a future 2.6 has to be chosen,
|
|
61
|
+
and re-gated, by a human.
|
|
47
62
|
|
|
48
|
-
The
|
|
49
|
-
in the root project, so an `overrides` entry here would not reach consuming apps.
|
|
50
|
-
satisfies `@nuxt/ui`'s own range, so there is one hoisted copy and no duplication.
|
|
63
|
+
The pin is in `dependencies` rather than `overrides` on purpose: npm honours `overrides` only
|
|
64
|
+
in the root project, so an `overrides` entry here would not reach consuming apps.
|
|
51
65
|
|
|
52
|
-
|
|
53
|
-
`failed to load icon` in the server log
|
|
54
|
-
|
|
66
|
+
**Any change to this version must be re-gated with `npm run test:icons`**, which boots the built
|
|
67
|
+
playground and fails on a single `failed to load icon` in the server log. Icons resolve at
|
|
68
|
+
runtime, per request — lint, typecheck and the build all stay green with every glyph missing,
|
|
69
|
+
which is exactly why this is easy to miss.
|
|
55
70
|
|
|
56
71
|
---
|
|
57
72
|
|
|
@@ -76,16 +91,28 @@ shell components wherever you like:
|
|
|
76
91
|
<template>
|
|
77
92
|
<div class="flex min-h-svh bg-default text-default">
|
|
78
93
|
<TspSidebar>
|
|
79
|
-
<template #brand
|
|
94
|
+
<template #brand>
|
|
95
|
+
<TspBrandTile><img src="/logo.svg" alt="trips"></TspBrandTile>
|
|
96
|
+
<TspWordmark name="trips" />
|
|
97
|
+
</template>
|
|
80
98
|
<template #nav>
|
|
81
|
-
<TspNavItem to="/" icon="i-tabler-car">Fahrten</TspNavItem>
|
|
82
|
-
<
|
|
99
|
+
<TspNavItem to="/" icon="i-tabler-car" badge="Owner" badge-tone="primary">Fahrten</TspNavItem>
|
|
100
|
+
<TspNavGroup to="/reports" icon="i-tabler-file-text" label="Berichte">
|
|
101
|
+
<TspNavSubItem to="/reports/tax" icon="i-tabler-receipt">Steuer</TspNavSubItem>
|
|
102
|
+
</TspNavGroup>
|
|
103
|
+
<TspNavSection label="Extern" />
|
|
104
|
+
<TspNavItem to="/handbook" icon="i-tabler-book">Handbuch</TspNavItem>
|
|
83
105
|
</template>
|
|
84
106
|
<template #footer>
|
|
85
|
-
<TspSidebarFooter
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
107
|
+
<TspSidebarFooter
|
|
108
|
+
v-model="locale"
|
|
109
|
+
app-name="trips"
|
|
110
|
+
user="fil" user-to="/user"
|
|
111
|
+
settings-to="/settings" logout-href="/auth/logout"
|
|
112
|
+
:locales="['de', 'en']"
|
|
113
|
+
github-link="https://github.com/…" version="v1.2.0" version-link="https://github.com/…/releases/tag/v1.2.0"
|
|
114
|
+
tip-jar tool-of
|
|
115
|
+
/>
|
|
89
116
|
</template>
|
|
90
117
|
</TspSidebar>
|
|
91
118
|
<main class="flex-1 min-w-0 bg-default">
|
|
@@ -144,20 +171,26 @@ scope:
|
|
|
144
171
|
|---|---|---|
|
|
145
172
|
| `primary` (amber) | `#fbad18` | `#fbad18` |
|
|
146
173
|
| `error` (tomato) | `#ff6347` | `#ff6347` |
|
|
147
|
-
| `bg` | `#
|
|
148
|
-
| `
|
|
174
|
+
| `bg` (page) | `#f1f3f5` | `#212529` |
|
|
175
|
+
| `bg-muted` (the shell) | `#f8f9fa` | `#2b3035` |
|
|
176
|
+
| `bg-elevated` (raised content) | `#ffffff` | `#31373e` |
|
|
149
177
|
| `border` | `#dee2e6` | `#404041` |
|
|
150
178
|
| `text` | `#212529` | `#f0f0f0` |
|
|
151
179
|
| `on-primary` (`text-inverted`) | `#212529` | `#212529` |
|
|
152
180
|
|
|
153
|
-
Brand colours (amber, tomato) are the same in both modes; only
|
|
181
|
+
Brand colours (amber, tomato) are the same in both modes; only surfaces, borders and text flip.
|
|
182
|
+
**Three rungs, not two** — the shell uses `bg-muted`, raised content uses `bg-elevated`. See
|
|
183
|
+
[`docs/design-system.md`](docs/design-system.md) for the full ladder and the reason it changed.
|
|
154
184
|
The amber ramp is also available as Tailwind utilities (`*-tsp-amber-{50..950}`).
|
|
155
185
|
|
|
156
186
|
**Fonts** — Nunito (text + headings) and Space Grotesk (logo), via `@nuxt/fonts`. Use the
|
|
157
187
|
`tsp-wordmark` class (or `<TspWordmark>`) for Space Grotesk; everything else is Nunito.
|
|
158
188
|
|
|
159
|
-
**
|
|
160
|
-
for the themed area
|
|
189
|
+
**Composables** (auto-imported) — `useTspColorMode()` → `{ pref, toggle }`, the remembered
|
|
190
|
+
(cookie) preference for the themed area; `useTspNavDrawer()` → `{ isOpen, open, close, toggle }`,
|
|
191
|
+
the mobile drawer's shared state; `useTspCopy()` → `{ copied, copy }`, clipboard plus a moment of
|
|
192
|
+
confirmation, with a fallback for non-secure contexts; `useTspReveal()` → `{ revealed, toggle,
|
|
193
|
+
mask }`, show/hide a secret with a fixed-length placeholder.
|
|
161
194
|
|
|
162
195
|
**Components** (auto-imported):
|
|
163
196
|
|
|
@@ -165,16 +198,42 @@ for the themed area.
|
|
|
165
198
|
|---|---|
|
|
166
199
|
| `TspThemeProvider` | Scoped wrapper — marks a sub-area as themed (`.tsp-theme`). |
|
|
167
200
|
| `TspSidebar` | App-shell sidebar frame; slots `#brand` `#nav` `#footer`. |
|
|
168
|
-
| `TspSidebarFooter` | User → Logout → divider → theme
|
|
201
|
+
| `TspSidebarFooter` | User → Settings → Logout → divider → theme + language → meta icons + version → "A tool of tsp.tools". Rows are **props**, not slots. |
|
|
169
202
|
| `TspSiteHeader` | Public/marketing header; slots `#brand` `#nav` `#actions`, prop `sticky`. |
|
|
170
203
|
| `TspSiteFooter` | Public/marketing footer; slots `#brand` `#links` `#actions`. |
|
|
171
204
|
| `TspThemeToggle` | Sun/moon light–dark toggle (uses `useTspColorMode`). |
|
|
172
|
-
| `TspNavItem` | Sidebar nav link
|
|
173
|
-
| `
|
|
205
|
+
| `TspNavItem` | Sidebar nav link — **white at rest, amber text when active**. Optional `badge`. |
|
|
206
|
+
| `TspNavGroup` | A nav item with always-visible sub-items on a hairline spine. |
|
|
207
|
+
| `TspNavSubItem` | A child row — muted, own icon, amber when active. |
|
|
208
|
+
| `TspNavSection` | A rule, optionally labelled (`External`). |
|
|
209
|
+
| `TspNavBadge` | Outline pill on a nav row; tones `primary` \| `neutral`. |
|
|
210
|
+
| `TspNavLabel` | Truncates a label and reveals it in full on hover. Used by the rows above. |
|
|
211
|
+
| `TspBrandTile` | The frame around a service's mark — `sidebar` (24px) or `hero` (64/96px). |
|
|
212
|
+
| `TspUserCard` | Account card for a service's user page; masked id + manage link. |
|
|
213
|
+
| `TspFooterRow` | One sidebar-footer row — `to` / `href` / `button`. |
|
|
214
|
+
| `TspLanguageToggle` | DE/EN segmented control; all strings are props. |
|
|
215
|
+
| `TspVersionBadge` | Version pill with a tag icon; a link only when given `href`. |
|
|
216
|
+
| `TspWordmark` | `<TspWordmark name="trips" />` → white name + amber dot. |
|
|
217
|
+
| `TspTopBar` | Mobile top bar — burger, brand, page title. Pairs with `TspSidebar`. |
|
|
218
|
+
| `TspSectionCard` | Icon + title over content, on the raised surface. |
|
|
219
|
+
| `TspPageTabs` | Area tabs as routes, so the back button works. |
|
|
220
|
+
| `TspCopyField` | Read-only value + copy; `secret` masks it behind a reveal. |
|
|
221
|
+
| `TspCopyButton` | Copy with a tick confirmation. |
|
|
222
|
+
| `TspCodeBlock` | Config to copy; shown and copied values can differ. |
|
|
223
|
+
| `TspBanner` | Dismissible strip; emits `dismiss`, remembering it is the app's job. |
|
|
224
|
+
| `TspReleaseNotes` | Release notes; props mirror the `tsp-release` skill's shape. |
|
|
225
|
+
| `TspToolOf` | "A tool of tsp.tools". Shared by both footers. |
|
|
174
226
|
| `TspContainer` | Centered content column; props `width` (`app` \| `site`) and `padded`. |
|
|
175
227
|
|
|
176
|
-
App-specific content (branding, nav lists, user identity, GitHub/version values) is injected
|
|
177
|
-
|
|
228
|
+
App-specific content (branding, nav lists, user identity, GitHub/version values) is injected via
|
|
229
|
+
props — none of it lives in the layer. **Prefer props over slots** for the footer rows every app
|
|
230
|
+
has: a slot invites each app to restyle its own copy, which is the drift `TspFooterRow` exists to
|
|
231
|
+
stop. `#user` survives only for apps needing more than a name.
|
|
232
|
+
|
|
233
|
+
**Open source is derived from `github-link`.** Set it and the meta row shows a GitHub icon and the
|
|
234
|
+
version badge links to the release; leave it out and the badge is plain text — **`version-link`
|
|
235
|
+
is then ignored**, because a closed-source project has no release page to point at. There is no
|
|
236
|
+
`variant` prop; a second source of truth could disagree with the first.
|
|
178
237
|
|
|
179
238
|
### App shell vs site shell
|
|
180
239
|
|
|
@@ -183,20 +242,21 @@ use `TspSiteHeader` + `TspSiteFooter` with the wide container:
|
|
|
183
242
|
|
|
184
243
|
```vue
|
|
185
244
|
<TspSiteHeader>
|
|
186
|
-
<template #brand><NuxtLink to="/"><TspWordmark
|
|
245
|
+
<template #brand><NuxtLink to="/"><TspWordmark name="trips" /></NuxtLink></template>
|
|
187
246
|
<template #nav><a href="#tools">Tools</a></template>
|
|
188
|
-
<template #actions><TspThemeToggle /></template>
|
|
247
|
+
<template #actions><TspThemeToggle icon-only /></template>
|
|
189
248
|
</TspSiteHeader>
|
|
190
249
|
|
|
191
250
|
<TspContainer width="site"><NuxtPage /></TspContainer>
|
|
192
251
|
|
|
193
252
|
<TspSiteFooter>
|
|
194
|
-
<template #brand><TspWordmark
|
|
253
|
+
<template #brand><TspWordmark name="trips" /></template>
|
|
195
254
|
<template #links><NuxtLink to="/imprint">Imprint</NuxtLink></template>
|
|
196
255
|
</TspSiteFooter>
|
|
197
256
|
```
|
|
198
257
|
|
|
199
|
-
`TspContainer` defaults to `width="app"` (`max-w-
|
|
258
|
+
`TspContainer` defaults to `width="app"` (`max-w-6xl` = 1152px, `px-6 sm:px-10`) — the app/admin
|
|
259
|
+
measure. Cap prose on the paragraph (`max-w-2xl`), not by narrowing the page.
|
|
200
260
|
`width="site"` is `max-w-[1240px]`, `px-6 md:px-10`, the marketing measure. Both add `pt-10 pb-16`
|
|
201
261
|
unless you pass `:padded="false"` (which is what the header/footer frames do internally).
|
|
202
262
|
|
|
@@ -212,8 +272,10 @@ button shows and announces while dark:
|
|
|
212
272
|
/>
|
|
213
273
|
```
|
|
214
274
|
|
|
215
|
-
The defaults are
|
|
216
|
-
|
|
275
|
+
The visible defaults are **`Light` / `Dark`** — one word, because "mode" earns nothing beside a
|
|
276
|
+
sun icon and the label shares a 224px row with the language control. The aria-labels keep the
|
|
277
|
+
full "Switch to light mode". Pass `icon-only` for a compact icon button (the site header and
|
|
278
|
+
tight rows); the default slot still receives `{ isDark, label }` if you want other markup.
|
|
217
279
|
|
|
218
280
|
---
|
|
219
281
|
|
|
@@ -265,18 +327,60 @@ They are excluded from the published package (`files` allowlist).
|
|
|
265
327
|
|
|
266
328
|
## Release
|
|
267
329
|
|
|
268
|
-
|
|
330
|
+
One command (ADR 005):
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
npm run bump-version
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
It runs the full gate, then `changelogen --release` writes `CHANGELOG.md`, bumps
|
|
337
|
+
`package.json`, commits and tags `vX.Y.Z`, then pushes the commit and the tag.
|
|
338
|
+
|
|
339
|
+
**The pushed tag is what publishes.** `.github/workflows/release.yml` re-runs the gate on a
|
|
340
|
+
clean checkout of the tagged commit and publishes to npm with provenance, so the tarball eight
|
|
341
|
+
services install is built from a tagged commit rather than from whatever a laptop had in its
|
|
342
|
+
working tree. It also refuses to publish if the tag and `package.json` disagree.
|
|
269
343
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
344
|
+
| Command | |
|
|
345
|
+
|---|---|
|
|
346
|
+
| `npm run gate` | everything CI runs — lint, `docs:api:check`, typecheck, icons, E2E |
|
|
347
|
+
| `npm run bump-version` | the release: gate → changelog + version + tag → push |
|
|
348
|
+
| `npm run release` | break-glass — gate then `npm publish` from here, for when Actions is down |
|
|
349
|
+
|
|
350
|
+
**There is no npm token.** The package uses npm **trusted publishing**: npmjs.com is configured
|
|
351
|
+
to trust this repository and this workflow file, GitHub mints a short-lived OIDC token for the
|
|
352
|
+
run, and npm verifies it. Nothing long-lived is stored in the repo, so there is no secret to
|
|
353
|
+
rotate and none to leak. Two consequences worth knowing:
|
|
354
|
+
|
|
355
|
+
- `id-token: write` in `release.yml` **is** the authentication. Remove it and the publish has no
|
|
356
|
+
way to authenticate at all.
|
|
357
|
+
- The workflow upgrades npm before publishing. Node 22 bundles npm 10, which has no OIDC support
|
|
358
|
+
and would silently look for a token that does not exist; trusted publishing needs npm ≥ 11.5.1
|
|
359
|
+
and Node ≥ 22.14.0, and the step fails loudly rather than falling back.
|
|
360
|
+
|
|
361
|
+
**There is no provenance, and that is not an oversight.** npm only signs provenance for a
|
|
362
|
+
**public** source repository; this one is private, so npm skips the attestation. Asking for it
|
|
363
|
+
explicitly does not produce one — it fails the publish with a 422 (`Unsupported GitHub Actions
|
|
364
|
+
source repository visibility: "private"`), which is how the first `v0.3.1` attempt died. If this
|
|
365
|
+
repository is ever made public, set `publishConfig.provenance` and restore the workflow step that
|
|
366
|
+
checks the attestation actually landed.
|
|
367
|
+
|
|
368
|
+
Trusted publishing is unaffected and is the substantive win either way: there is no token to
|
|
369
|
+
leak, and the registry records the publisher as GitHub Actions over OIDC rather than a person.
|
|
370
|
+
|
|
371
|
+
**`0.3.0` is a special case: it needs a tag, not a bump.** Its version was already written into
|
|
372
|
+
`package.json` by hand (`0a5c793`) and its changelog entry is written, so two of changelogen's
|
|
373
|
+
three jobs are done. The third — `changelogen --release` — cannot run: it refuses a bump whose
|
|
374
|
+
result equals the current version (`bumpVersion` returns `false`, reported as "Unable to bump
|
|
375
|
+
version based on changes"), and `-r 0.3.0` on a package already at `0.3.0` is exactly that. So
|
|
376
|
+
tag it directly:
|
|
273
377
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
378
|
+
```bash
|
|
379
|
+
npm run gate && git tag -a v0.3.0 -m "v0.3.0" && git push --follow-tags
|
|
380
|
+
```
|
|
277
381
|
|
|
278
|
-
`npm run
|
|
279
|
-
so
|
|
382
|
+
From `0.3.1` on, `npm run bump-version` is the path — changelogen picks the next version from the
|
|
383
|
+
commits, so there is nothing to force.
|
|
280
384
|
|
|
281
385
|
## License
|
|
282
386
|
|
package/assets/css/theme.css
CHANGED
|
@@ -89,15 +89,39 @@
|
|
|
89
89
|
modes (the `on-primary` token). */
|
|
90
90
|
--ui-text-inverted: #212529;
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
/* Brand amber used as TEXT on the current surface (not as a fill). Amber-400
|
|
93
|
+
is the brand primary and is correct on the dark surfaces; on light it fails
|
|
94
|
+
text contrast, so the light group steps down to amber-600 — see
|
|
95
|
+
design-system.md, accessibility note. Fills keep `--ui-primary`. */
|
|
96
|
+
--tsp-text-brand: var(--color-tsp-amber-400);
|
|
97
|
+
|
|
98
|
+
/* Radius base. Nuxt UI derives its whole scale from this:
|
|
99
|
+
rounded-sm = 1x = 4px · rounded-md = 1.5x = 6px
|
|
100
|
+
rounded-lg = 2x = 8px · rounded-xl = 3x = 12px
|
|
101
|
+
0.25rem is what makes `rounded-xl` the 12px card radius that
|
|
102
|
+
design-system.md and ADR 018 specify, and `rounded-md` the 6px shell-row
|
|
103
|
+
radius. The previous 0.5rem is why cards rendered 24px (ADR 018 read that
|
|
104
|
+
as gage drift; it was this token). */
|
|
105
|
+
--ui-radius: 0.25rem;
|
|
93
106
|
}
|
|
94
107
|
|
|
95
|
-
/* Dark surfaces (default) — tsp anthracite.
|
|
108
|
+
/* Dark surfaces (default) — tsp anthracite.
|
|
109
|
+
|
|
110
|
+
THREE RUNGS, NOT TWO. `muted` and `elevated` used to be the same #2b3035,
|
|
111
|
+
which left no surface for a raised card: it either matched the page or
|
|
112
|
+
matched the sidebar, and it matched the sidebar in stash, gage and trips.
|
|
113
|
+
The ladder is now
|
|
114
|
+
bg page
|
|
115
|
+
muted the shell — sidebar, top bar
|
|
116
|
+
elevated raised CONTENT — cards, the user card, feature boxes
|
|
117
|
+
accented pressed/selected chrome
|
|
118
|
+
and the shell sits below content rather than beside it, which is also what
|
|
119
|
+
ADR 018 wanted from a quiet shell. */
|
|
96
120
|
.dark .tsp-theme,
|
|
97
121
|
.dark.tsp-theme {
|
|
98
122
|
--ui-bg: #212529;
|
|
99
123
|
--ui-bg-muted: #2b3035;
|
|
100
|
-
--ui-bg-elevated: #
|
|
124
|
+
--ui-bg-elevated: #31373e;
|
|
101
125
|
--ui-bg-accented: #404041;
|
|
102
126
|
--ui-bg-inverted: #f0f0f0;
|
|
103
127
|
|
|
@@ -113,11 +137,16 @@
|
|
|
113
137
|
--ui-text-highlighted: #ffffff;
|
|
114
138
|
}
|
|
115
139
|
|
|
116
|
-
/* Light surfaces — tsp light theme (design-system.md → Light theme).
|
|
140
|
+
/* Light surfaces — tsp light theme (design-system.md → Light theme).
|
|
141
|
+
|
|
142
|
+
Same three rungs. Light had the same collapse (muted and elevated both
|
|
143
|
+
#ffffff) and nowhere left to go, because the page was already near-white:
|
|
144
|
+
the page steps down to #f1f3f5 so the shell can take #f8f9fa and a raised
|
|
145
|
+
card can be pure white. Raised means lighter here, the inverse of dark. */
|
|
117
146
|
.light .tsp-theme,
|
|
118
147
|
.light.tsp-theme {
|
|
119
|
-
--ui-bg: #
|
|
120
|
-
--ui-bg-muted: #
|
|
148
|
+
--ui-bg: #f1f3f5;
|
|
149
|
+
--ui-bg-muted: #f8f9fa;
|
|
121
150
|
--ui-bg-elevated: #ffffff;
|
|
122
151
|
--ui-bg-accented: #dee2e6;
|
|
123
152
|
--ui-bg-inverted: #212529;
|
|
@@ -132,6 +161,57 @@
|
|
|
132
161
|
--ui-text-toned: #343a40;
|
|
133
162
|
--ui-text: #212529;
|
|
134
163
|
--ui-text-highlighted: #16191c;
|
|
164
|
+
|
|
165
|
+
/* #fbad18 fails contrast as text on these surfaces; #f47920 passes. */
|
|
166
|
+
--tsp-text-brand: var(--color-tsp-amber-600);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/* Mode-correct brand text (ADR 018: the active nav item is amber TEXT, not an
|
|
170
|
+
amber fill). Written unlayered and with two classes so it beats Tailwind's
|
|
171
|
+
`text-muted` / `hover:text-*` utilities without needing `!important`. */
|
|
172
|
+
.tsp-theme .tsp-text-brand {
|
|
173
|
+
color: var(--tsp-text-brand);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Mode-correct brand colour as a BORDER, for the same reason .tsp-text-brand
|
|
177
|
+
exists for text: #fbad18 is the brand, but the light group steps down to
|
|
178
|
+
amber-600 so an accent edge holds up on light surfaces too. */
|
|
179
|
+
.tsp-theme .tsp-border-brand {
|
|
180
|
+
border-left-color: var(--tsp-text-brand);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* Reserve the scrollbar's width whether or not the page scrolls.
|
|
184
|
+
|
|
185
|
+
The content column is centred, so it moves by HALF the scrollbar width the
|
|
186
|
+
moment a page gets tall enough to overflow: walking from a short page to a
|
|
187
|
+
long one shifted every heading, card and table sideways. Measured in Chrome
|
|
188
|
+
at 1400px, the playground put the column at 364px on a short route and at
|
|
189
|
+
356.5px on a long one.
|
|
190
|
+
|
|
191
|
+
Both selectors are the same rule in the two consumption modes: GLOBAL puts
|
|
192
|
+
the scope class on <html> itself, SCOPED puts it on a provider inside the
|
|
193
|
+
page. Neither matches a consumer's own public pages, which this layer does
|
|
194
|
+
not own and must not relayout.
|
|
195
|
+
|
|
196
|
+
Browsers with overlay scrollbars (macOS default) are unaffected — there is no
|
|
197
|
+
width to reserve, so nothing changes for them either way. */
|
|
198
|
+
html.tsp-theme,
|
|
199
|
+
html:has(.tsp-theme) {
|
|
200
|
+
scrollbar-gutter: stable;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* Scrollable, but without a visible scrollbar track. The sidebar nav has to
|
|
204
|
+
scroll when a service has a long nav or a short window, but a permanent
|
|
205
|
+
track beside 32px rows in a 224px column reads as clutter — and on platforms
|
|
206
|
+
with overlay scrollbars it appears and disappears as you move the mouse,
|
|
207
|
+
which is worse. Scrolling itself is untouched: wheel, trackpad, touch and
|
|
208
|
+
keyboard all still work. */
|
|
209
|
+
.tsp-scroll-quiet {
|
|
210
|
+
scrollbar-width: none;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.tsp-scroll-quiet::-webkit-scrollbar {
|
|
214
|
+
display: none;
|
|
135
215
|
}
|
|
136
216
|
|
|
137
217
|
/* The wordmark uses Space Grotesk; everything else stays Nunito. */
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
A dismissible strip across the top of the content area. Its first job is the
|
|
3
|
+
release announcement the tsp-release skill's `showBanner` flag turns on, but
|
|
4
|
+
nothing here is release-specific: it is a banner.
|
|
5
|
+
|
|
6
|
+
<TspBanner icon="i-tabler-sparkles" title="Version 1.3.0">
|
|
7
|
+
Neue Auswertung im Bericht.
|
|
8
|
+
<template #action><UButton …>Was ist neu?</UButton></template>
|
|
9
|
+
</TspBanner>
|
|
10
|
+
|
|
11
|
+
Amber TEXT and an amber left edge, not an amber fill: ADR 018 gives the fill
|
|
12
|
+
to primary buttons alone, and a full-width amber bar would be the loudest
|
|
13
|
+
thing on the page by a distance.
|
|
14
|
+
|
|
15
|
+
Dismissal is the CALLER's to remember. The banner emits `dismiss`; whether
|
|
16
|
+
that is per release, per user or per session is a question the layer cannot
|
|
17
|
+
answer, and guessing it wrong means a banner that never comes back or never
|
|
18
|
+
goes away.
|
|
19
|
+
-->
|
|
20
|
+
<template>
|
|
21
|
+
<div
|
|
22
|
+
v-if="!dismissed"
|
|
23
|
+
data-testid="tsp-banner"
|
|
24
|
+
role="status"
|
|
25
|
+
class="flex items-start gap-3 rounded-xl border border-default border-l-2 bg-elevated px-4 py-3"
|
|
26
|
+
:class="tone === 'brand' ? 'tsp-border-brand' : ''"
|
|
27
|
+
>
|
|
28
|
+
<UIcon
|
|
29
|
+
v-if="icon"
|
|
30
|
+
:name="icon"
|
|
31
|
+
class="mt-0.5 shrink-0 text-lg"
|
|
32
|
+
:class="tone === 'brand' ? 'tsp-text-brand' : 'text-muted'"
|
|
33
|
+
/>
|
|
34
|
+
|
|
35
|
+
<div class="min-w-0 flex-1">
|
|
36
|
+
<p
|
|
37
|
+
v-if="title"
|
|
38
|
+
data-testid="tsp-banner-title"
|
|
39
|
+
class="text-sm font-bold text-highlighted"
|
|
40
|
+
>
|
|
41
|
+
{{ title }}
|
|
42
|
+
</p>
|
|
43
|
+
<div class="text-sm text-muted">
|
|
44
|
+
<slot />
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<div class="flex shrink-0 items-center gap-2">
|
|
49
|
+
<slot name="action" />
|
|
50
|
+
<button
|
|
51
|
+
v-if="dismissible"
|
|
52
|
+
type="button"
|
|
53
|
+
data-testid="tsp-banner-dismiss"
|
|
54
|
+
:aria-label="dismissLabel"
|
|
55
|
+
:title="dismissLabel"
|
|
56
|
+
class="flex size-8 items-center justify-center rounded-md text-muted hover:text-highlighted hover:bg-default"
|
|
57
|
+
@click="dismiss"
|
|
58
|
+
>
|
|
59
|
+
<UIcon
|
|
60
|
+
name="i-tabler-x"
|
|
61
|
+
class="text-lg"
|
|
62
|
+
/>
|
|
63
|
+
</button>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</template>
|
|
67
|
+
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
withDefaults(defineProps<{
|
|
70
|
+
/** Headline. Without it the banner is only its default slot. */
|
|
71
|
+
title?: string
|
|
72
|
+
/** Leading icon, `i-tabler-*`. */
|
|
73
|
+
icon?: string
|
|
74
|
+
/** `brand` = amber accent edge, `neutral` = quiet. Neither is a fill. */
|
|
75
|
+
tone?: 'brand' | 'neutral'
|
|
76
|
+
/** Show the close button. The app decides whether it STAYS closed. */
|
|
77
|
+
dismissible?: boolean
|
|
78
|
+
/** Accessible name for the close button. */
|
|
79
|
+
dismissLabel?: string
|
|
80
|
+
}>(), {
|
|
81
|
+
title: undefined,
|
|
82
|
+
icon: undefined,
|
|
83
|
+
tone: 'brand',
|
|
84
|
+
dismissible: true,
|
|
85
|
+
dismissLabel: 'Dismiss',
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const emit = defineEmits<{ dismiss: [] }>()
|
|
89
|
+
|
|
90
|
+
// Local only, so the banner disappears immediately on click. Whether it STAYS
|
|
91
|
+
// gone is the app's decision, via the event.
|
|
92
|
+
const dismissed = ref(false)
|
|
93
|
+
const dismiss = () => {
|
|
94
|
+
dismissed.value = true
|
|
95
|
+
emit('dismiss')
|
|
96
|
+
}
|
|
97
|
+
</script>
|