@treeseed/core 0.8.7 → 0.8.9

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.
Files changed (73) hide show
  1. package/dist/components/content/ContentStatusLegend.astro +4 -4
  2. package/dist/components/docs/BookFontControls.astro +9 -9
  3. package/dist/components/docs/DesktopSidebarToggle.astro +8 -8
  4. package/dist/components/docs/Footer.astro +6 -6
  5. package/dist/components/docs/PageTitle.astro +1 -1
  6. package/dist/components/docs/ThemeSelect.astro +3 -1
  7. package/dist/components/forms/ContactForm.astro +21 -21
  8. package/dist/components/forms/FooterSubscribeForm.astro +9 -9
  9. package/dist/components/site/BookList.astro +7 -7
  10. package/dist/components/site/CTASection.astro +4 -4
  11. package/dist/components/site/ChronicleList.astro +6 -6
  12. package/dist/components/site/Hero.astro +3 -3
  13. package/dist/components/site/PathCard.astro +5 -5
  14. package/dist/components/site/ProfileList.astro +5 -5
  15. package/dist/components/site/RouteNotFound.astro +5 -5
  16. package/dist/components/site/SectionIntro.astro +3 -3
  17. package/dist/components/site/StageBanner.astro +2 -2
  18. package/dist/components/site/TrustCallout.astro +3 -3
  19. package/dist/components/ui/data/ActionList.astro +51 -0
  20. package/dist/components/ui/data/Badge.astro +19 -0
  21. package/dist/components/ui/data/DataTable.astro +51 -0
  22. package/dist/components/ui/data/KeyValueList.astro +28 -0
  23. package/dist/components/ui/data/MetricCard.astro +25 -0
  24. package/dist/components/ui/data/MetricGrid.astro +27 -0
  25. package/dist/components/ui/data/StatusPill.astro +20 -0
  26. package/dist/components/ui/forms/Button.astro +52 -0
  27. package/dist/components/ui/forms/Field.astro +39 -0
  28. package/dist/components/ui/forms/FormActions.astro +12 -0
  29. package/dist/components/ui/forms/PasswordMeter.astro +80 -0
  30. package/dist/components/ui/forms/RadioGroup.astro +55 -0
  31. package/dist/components/ui/forms/Select.astro +44 -0
  32. package/dist/components/ui/forms/TextInput.astro +58 -0
  33. package/dist/components/ui/forms/Textarea.astro +45 -0
  34. package/dist/components/ui/layout/PageHeader.astro +45 -0
  35. package/dist/components/ui/shell/AppShell.astro +110 -0
  36. package/dist/components/ui/shell/BottomNav.astro +35 -0
  37. package/dist/components/ui/shell/ProjectHeader.astro +66 -0
  38. package/dist/components/ui/shell/PublicShell.astro +108 -0
  39. package/dist/components/ui/shell/RailNav.astro +35 -0
  40. package/dist/components/ui/shell/TopBar.astro +52 -0
  41. package/dist/components/ui/surface/Card.astro +46 -0
  42. package/dist/components/ui/surface/EmptyState.astro +45 -0
  43. package/dist/components/ui/surface/Panel.astro +54 -0
  44. package/dist/components/ui/theme/ThemeMenu.astro +32 -0
  45. package/dist/components/ui/theme/ThemePreviewSwatch.astro +18 -0
  46. package/dist/components/ui/theme/ThemeScript.astro +105 -0
  47. package/dist/components/ui/theme/ThemeSelector.astro +202 -0
  48. package/dist/components/ui/types.js +0 -0
  49. package/dist/dev.js +14 -2
  50. package/dist/layouts/AuthoredEntryLayout.astro +27 -27
  51. package/dist/layouts/BookLayout.astro +10 -10
  52. package/dist/layouts/ContentLayout.astro +4 -4
  53. package/dist/layouts/MainLayout.astro +27 -25
  54. package/dist/layouts/NoteLayout.astro +6 -6
  55. package/dist/layouts/ProfileLayout.astro +17 -17
  56. package/dist/pages/404.astro +6 -6
  57. package/dist/pages/contact.astro +4 -4
  58. package/dist/pages/docs-runtime/[...slug].astro +12 -12
  59. package/dist/pages/docs-runtime/index.astro +13 -13
  60. package/dist/pages/index.astro +28 -28
  61. package/dist/pages/ui/index.astro +216 -0
  62. package/dist/site.js +3 -2
  63. package/dist/styles/app-shell.css +398 -0
  64. package/dist/styles/forms.css +258 -0
  65. package/dist/styles/global.css +119 -119
  66. package/dist/styles/prose.css +11 -11
  67. package/dist/styles/theme.css +177 -0
  68. package/dist/styles/tokens.css +62 -22
  69. package/dist/styles/ui.css +551 -0
  70. package/dist/utils/content-status.js +5 -5
  71. package/dist/utils/site-config.js +2 -2
  72. package/dist/utils/theme.js +352 -40
  73. package/package.json +35 -2
@@ -0,0 +1,216 @@
1
+ ---
2
+ import '../../styles/global.css';
3
+ import '../../styles/theme.css';
4
+ import '../../styles/ui.css';
5
+ import '../../styles/forms.css';
6
+ import ThemeScript from '../../components/ui/theme/ThemeScript.astro';
7
+ import ThemeSelector from '../../components/ui/theme/ThemeSelector.astro';
8
+ import Button from '../../components/ui/forms/Button.astro';
9
+ import Field from '../../components/ui/forms/Field.astro';
10
+ import TextInput from '../../components/ui/forms/TextInput.astro';
11
+ import Select from '../../components/ui/forms/Select.astro';
12
+ import Textarea from '../../components/ui/forms/Textarea.astro';
13
+ import RadioGroup from '../../components/ui/forms/RadioGroup.astro';
14
+ import FormActions from '../../components/ui/forms/FormActions.astro';
15
+ import PasswordMeter from '../../components/ui/forms/PasswordMeter.astro';
16
+ import Panel from '../../components/ui/surface/Panel.astro';
17
+ import Card from '../../components/ui/surface/Card.astro';
18
+ import EmptyState from '../../components/ui/surface/EmptyState.astro';
19
+ import Badge from '../../components/ui/data/Badge.astro';
20
+ import StatusPill from '../../components/ui/data/StatusPill.astro';
21
+ import MetricGrid from '../../components/ui/data/MetricGrid.astro';
22
+ import ActionList from '../../components/ui/data/ActionList.astro';
23
+ import KeyValueList from '../../components/ui/data/KeyValueList.astro';
24
+ import DataTable from '../../components/ui/data/DataTable.astro';
25
+ import PageHeader from '../../components/ui/layout/PageHeader.astro';
26
+ import { getBuiltInColorSchemes, normalizeThemePreference } from '../../utils/theme.js';
27
+
28
+ const selected = normalizeThemePreference({
29
+ scheme: Astro.url.searchParams.get('scheme'),
30
+ mode: Astro.url.searchParams.get('mode'),
31
+ });
32
+ const schemes = getBuiltInColorSchemes();
33
+ const modes = ['system', 'light', 'dark'] as const;
34
+ const tones = ['default', 'muted', 'accent', 'info', 'success', 'warning', 'danger'] as const;
35
+ const themeLinks = schemes.flatMap((scheme) => modes.map((mode) => ({
36
+ label: `${scheme.name} / ${mode}`,
37
+ href: `/ui/?scheme=${scheme.id}&mode=${mode}`,
38
+ active: scheme.id === selected.scheme && mode === selected.mode,
39
+ })));
40
+ const projectMetrics = [
41
+ { label: 'Open decisions', value: 7, description: 'Three are blocked by missing context.', tone: 'warning' as const },
42
+ { label: 'Ready releases', value: 2, description: 'Both have verification notes attached.', tone: 'success' as const },
43
+ { label: 'Active hosts', value: 14, description: 'Capacity is steady across the team.', tone: 'info' as const },
44
+ ];
45
+ const actionItems = [
46
+ {
47
+ title: 'Review release candidate',
48
+ description: 'Confirm the staged package summary before the next handoff.',
49
+ href: '#',
50
+ meta: 'Waiting',
51
+ tone: 'warning' as const,
52
+ actionLabel: 'Open',
53
+ },
54
+ {
55
+ title: 'Resolve shared direction',
56
+ description: 'One proposal needs a final owner note.',
57
+ href: '#',
58
+ meta: 'Decision',
59
+ tone: 'accent' as const,
60
+ actionLabel: 'Review',
61
+ },
62
+ {
63
+ title: 'Host capacity checked',
64
+ description: 'No action required after the latest sync.',
65
+ meta: 'Clear',
66
+ tone: 'success' as const,
67
+ },
68
+ ];
69
+ const tableColumns = [
70
+ { key: 'name', label: 'Name' },
71
+ { key: 'status', label: 'Status' },
72
+ { key: 'owner', label: 'Owner' },
73
+ ];
74
+ const tableRows = [
75
+ { name: 'Direction brief', status: 'Ready', owner: 'Research' },
76
+ { name: 'Release notes', status: 'Drafting', owner: 'Platform' },
77
+ { name: 'Host review', status: 'Queued', owner: 'Ops' },
78
+ ];
79
+ ---
80
+
81
+ <!doctype html>
82
+ <html lang="en" data-ts-scheme={selected.scheme} data-ts-mode={selected.mode}>
83
+ <head>
84
+ <meta charset="UTF-8" />
85
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
86
+ <title>TreeSeed UI Catalog</title>
87
+ <ThemeScript defaultScheme={selected.scheme} defaultMode={selected.mode} />
88
+ </head>
89
+ <body>
90
+ <main class="ts-ui-catalog">
91
+ <PageHeader
92
+ eyebrow="Core UI"
93
+ title="TreeSeed primitive catalog"
94
+ description="A focused review surface for the first shared component set. Use the appearance controls and scheme links to inspect the same primitives across every built-in theme."
95
+ actions={[{ label: 'Back home', href: '/', variant: 'secondary' }]}
96
+ >
97
+ <div class="ts-ui-catalog__selector">
98
+ <ThemeSelector selectedScheme={selected.scheme} selectedMode={selected.mode} compact={false} />
99
+ </div>
100
+ </PageHeader>
101
+
102
+ <Panel title="Scheme and mode checks" description="Each link reloads this same catalog with a different default appearance.">
103
+ <div class="ts-ui-catalog__theme-links">
104
+ {themeLinks.map((link) => (
105
+ <Button href={link.href} variant={link.active ? 'primary' : 'secondary'} size="sm">
106
+ {link.label}
107
+ </Button>
108
+ ))}
109
+ </div>
110
+ </Panel>
111
+
112
+ <Panel title="Buttons, badges, and status" description="Tones are semantic; pages should not pass arbitrary color classes.">
113
+ <div class="ts-ui-catalog__stack">
114
+ <div class="ts-ui-catalog__row">
115
+ <Button>Primary action</Button>
116
+ <Button variant="secondary">Secondary</Button>
117
+ <Button variant="ghost">Ghost</Button>
118
+ <Button variant="danger">Danger</Button>
119
+ <Button disabled>Disabled</Button>
120
+ </div>
121
+ <div class="ts-ui-catalog__row">
122
+ {tones.map((tone) => <Badge tone={tone}>{tone}</Badge>)}
123
+ </div>
124
+ <div class="ts-ui-catalog__row">
125
+ {tones.map((tone) => <StatusPill tone={tone} label={tone} />)}
126
+ </div>
127
+ </div>
128
+ </Panel>
129
+
130
+ <MetricGrid metrics={projectMetrics} />
131
+
132
+ <div class="ts-ui-catalog__grid">
133
+ <Card
134
+ eyebrow="Card"
135
+ title="Direction summary"
136
+ description="Compact cards use the same border, surface, and tone language as panels."
137
+ tone="accent"
138
+ interactive
139
+ >
140
+ <StatusPill tone="success" label="Current" />
141
+ </Card>
142
+ <Card href="#" eyebrow="Linked card" title="Open proposal" description="Cards can render anchors when a route owns the whole surface.">
143
+ <Badge tone="info">Proposal</Badge>
144
+ </Card>
145
+ <EmptyState
146
+ eyebrow="Empty state"
147
+ title="No queued reviews"
148
+ description="The empty surface keeps actions visible without introducing a custom card style."
149
+ actions={[{ label: 'Create review', href: '#', variant: 'secondary' }]}
150
+ />
151
+ </div>
152
+
153
+ <Panel
154
+ title="Form controls"
155
+ description="Labels, help text, controls, radio choices, actions, and password feedback share one form vocabulary."
156
+ >
157
+ <form class="ts-ui-catalog__form">
158
+ <Field label="Project name" name="projectName" help="Use a short name that people can scan quickly.">
159
+ <TextInput name="projectName" value="Release Garden" />
160
+ </Field>
161
+ <Field label="Workspace" name="workspace">
162
+ <Select
163
+ name="workspace"
164
+ value="research"
165
+ options={[
166
+ { label: 'Research', value: 'research' },
167
+ { label: 'Operations', value: 'operations' },
168
+ { label: 'Platform', value: 'platform' },
169
+ ]}
170
+ />
171
+ </Field>
172
+ <Field label="Notes" name="notes" full>
173
+ <Textarea name="notes" value="Capture the next decision and keep the release handoff visible." />
174
+ </Field>
175
+ <RadioGroup
176
+ name="cadence"
177
+ legend="Review cadence"
178
+ value="weekly"
179
+ options={[
180
+ { label: 'Daily', value: 'daily', help: 'Best for launch week.' },
181
+ { label: 'Weekly', value: 'weekly', help: 'A good default for steady projects.' },
182
+ { label: 'Paused', value: 'paused', help: 'Hide reminders until work resumes.' },
183
+ ]}
184
+ />
185
+ <Field label="Password preview" name="catalogPassword" full>
186
+ <TextInput id="catalogPassword" name="catalogPassword" type="password" value="TreeSeed-2026" />
187
+ </Field>
188
+ <PasswordMeter inputId="catalogPassword" />
189
+ <FormActions align="between">
190
+ <Button variant="ghost" type="reset">Reset</Button>
191
+ <Button type="submit">Save example</Button>
192
+ </FormActions>
193
+ </form>
194
+ </Panel>
195
+
196
+ <div class="ts-ui-catalog__grid">
197
+ <Panel title="Action list" description="Rows remain dense but touch-friendly on mobile.">
198
+ <ActionList items={actionItems} />
199
+ </Panel>
200
+ <Panel title="Key details" description="Settings and summaries can use a consistent key/value rhythm.">
201
+ <KeyValueList
202
+ items={[
203
+ { key: 'Team', value: 'Canopy Systems' },
204
+ { key: 'Environment', value: 'Staging', tone: 'info' },
205
+ { key: 'Risk', value: 'Low', tone: 'success' },
206
+ ]}
207
+ />
208
+ </Panel>
209
+ </div>
210
+
211
+ <Panel title="Data table" description="The table collapses into labeled mobile rows without page-specific CSS.">
212
+ <DataTable columns={tableColumns} rows={tableRows} caption="Review artifacts" />
213
+ </Panel>
214
+ </main>
215
+ </body>
216
+ </html>
package/dist/site.js CHANGED
@@ -9,7 +9,7 @@ import tailwindcss from "@tailwindcss/vite";
9
9
  import { parseSiteConfig } from "./utils/site-config-schema.js";
10
10
  import { buildTenantBookRuntime } from "@treeseed/sdk/platform/books-data";
11
11
  import { getStarlightSidebarConfigFromRuntime } from "./utils/starlight-nav.js";
12
- import { buildTenantThemeCss } from "./utils/theme.js";
12
+ import { buildTreeseedThemeCss } from "./utils/theme.js";
13
13
  import { loadTreeseedDeployConfig } from "@treeseed/sdk/platform/deploy-config";
14
14
  import { getTreeseedContentServingMode } from "@treeseed/sdk/platform/deploy-runtime";
15
15
  import { loadTreeseedPluginRuntime } from "@treeseed/sdk/platform/plugins";
@@ -41,6 +41,7 @@ const PACKAGE_ROUTE_ENTRIES = [
41
41
  { pattern: "/404", resourcePath: "pages/404.astro" },
42
42
  { pattern: "/contact", resourcePath: "pages/contact.astro" },
43
43
  { pattern: "/feed.xml", resourcePath: "pages/feed.xml", model: "notes" },
44
+ { pattern: "/ui", resourcePath: "pages/ui/index.astro" },
44
45
  { pattern: "/[slug]", resourcePath: "pages/[slug].astro", model: "pages" },
45
46
  { pattern: "/agents", resourcePath: "pages/agents/index.astro", model: "agents" },
46
47
  { pattern: "/agents/[slug]", resourcePath: "pages/agents/[slug].astro", model: "agents" },
@@ -215,7 +216,7 @@ function createTreeseedSite(tenantConfig, { starlight }) {
215
216
  const bookRuntime = buildTenantBookRuntime(tenantConfig, { projectRoot });
216
217
  const docsRendered = isSiteRenderedModel(tenantConfig, "docs");
217
218
  const booksRendered = isSiteRenderedModel(tenantConfig, "books");
218
- const tenantThemeCss = buildTenantThemeCss(siteConfig.site.theme);
219
+ const tenantThemeCss = buildTreeseedThemeCss(siteConfig.site.theme);
219
220
  const siteLayers = buildTreeseedSiteLayers(pluginRuntime, {
220
221
  coreRoot: fileURLToPath(new URL(".", import.meta.url)),
221
222
  projectRoot,
@@ -0,0 +1,398 @@
1
+ .ts-skip-link {
2
+ background: var(--ts-color-accent);
3
+ border-radius: var(--ts-radius-md);
4
+ color: var(--ts-color-accent-text);
5
+ font-weight: 750;
6
+ left: var(--ts-space-3);
7
+ padding: var(--ts-space-2) var(--ts-space-3);
8
+ position: fixed;
9
+ top: var(--ts-space-3);
10
+ transform: translateY(-150%);
11
+ z-index: 100;
12
+ }
13
+
14
+ .ts-skip-link:focus {
15
+ transform: translateY(0);
16
+ }
17
+
18
+ .ts-app-shell {
19
+ background: var(--ts-color-canvas);
20
+ color: var(--ts-color-text);
21
+ display: grid;
22
+ grid-template-columns: 17.875rem minmax(0, 1fr);
23
+ min-height: 100vh;
24
+ }
25
+
26
+ .ts-app-shell__rail {
27
+ align-self: start;
28
+ background: var(--ts-color-canvas-subtle);
29
+ border-right: 1px solid var(--ts-color-border);
30
+ display: grid;
31
+ gap: var(--ts-space-3);
32
+ height: 100vh;
33
+ overflow: auto;
34
+ padding: var(--ts-space-3);
35
+ position: sticky;
36
+ top: 0;
37
+ }
38
+
39
+ .ts-shell-brand {
40
+ align-items: center;
41
+ color: var(--ts-color-text);
42
+ display: flex;
43
+ gap: var(--ts-space-2);
44
+ text-decoration: none;
45
+ }
46
+
47
+ .ts-shell-brand:hover {
48
+ text-decoration: none;
49
+ }
50
+
51
+ .ts-shell-brand__mark {
52
+ align-items: center;
53
+ display: inline-flex;
54
+ flex: 0 0 auto;
55
+ font-weight: 800;
56
+ height: 2rem;
57
+ justify-content: center;
58
+ width: 2rem;
59
+ }
60
+
61
+ .ts-shell-brand__mark img {
62
+ display: block;
63
+ height: 100%;
64
+ width: 100%;
65
+ }
66
+
67
+ .ts-shell-brand__text {
68
+ display: grid;
69
+ gap: 0.1rem;
70
+ min-width: 0;
71
+ }
72
+
73
+ .ts-shell-brand__name {
74
+ font-size: 0.95rem;
75
+ font-weight: 750;
76
+ line-height: 1.15;
77
+ }
78
+
79
+ .ts-shell-brand__tag {
80
+ color: var(--ts-color-text-muted);
81
+ font-size: 0.74rem;
82
+ font-weight: 500;
83
+ line-height: 1.25;
84
+ max-width: 13rem;
85
+ }
86
+
87
+ .ts-top-bar {
88
+ align-items: center;
89
+ display: flex;
90
+ gap: var(--ts-space-2);
91
+ justify-content: space-between;
92
+ min-width: 0;
93
+ }
94
+
95
+ .ts-top-bar__actions {
96
+ align-items: center;
97
+ display: flex;
98
+ flex-wrap: wrap;
99
+ gap: var(--ts-space-1);
100
+ justify-content: flex-end;
101
+ }
102
+
103
+ .ts-app-shell__rail-context,
104
+ .ts-app-shell__quick-actions {
105
+ border-top: 1px solid var(--ts-color-border);
106
+ display: grid;
107
+ gap: var(--ts-space-2);
108
+ padding-top: var(--ts-space-2);
109
+ }
110
+
111
+ .ts-app-shell__eyebrow {
112
+ color: var(--ts-color-text-subtle);
113
+ font-size: 0.75rem;
114
+ font-weight: 750;
115
+ margin: 0;
116
+ text-transform: uppercase;
117
+ }
118
+
119
+ .ts-app-shell__quick-list {
120
+ display: grid;
121
+ gap: var(--ts-space-2);
122
+ }
123
+
124
+ .ts-rail-nav {
125
+ border-top: 1px solid var(--ts-color-border);
126
+ display: grid;
127
+ gap: 0.2rem;
128
+ padding-top: var(--ts-space-2);
129
+ }
130
+
131
+ .ts-rail-nav__link,
132
+ .ts-bottom-nav__link,
133
+ .ts-public-shell__link,
134
+ .ts-shell-tab {
135
+ color: var(--ts-color-text-muted);
136
+ text-decoration: none;
137
+ }
138
+
139
+ .ts-rail-nav__link {
140
+ border-radius: var(--ts-radius-md);
141
+ display: flex;
142
+ font-weight: 650;
143
+ justify-content: space-between;
144
+ padding: 0.55rem 0.55rem;
145
+ }
146
+
147
+ .ts-rail-nav__link[aria-current='page'],
148
+ .ts-rail-nav__link:hover {
149
+ background: var(--ts-color-surface);
150
+ color: var(--ts-color-text);
151
+ text-decoration: none;
152
+ }
153
+
154
+ .ts-app-shell__main {
155
+ display: grid;
156
+ gap: var(--ts-space-3);
157
+ padding: var(--ts-space-3);
158
+ }
159
+
160
+ .ts-app-shell__mobile-top {
161
+ display: none;
162
+ }
163
+
164
+ .ts-app-shell__header {
165
+ align-items: flex-start;
166
+ display: flex;
167
+ gap: var(--ts-space-3);
168
+ justify-content: space-between;
169
+ }
170
+
171
+ .ts-app-shell__title {
172
+ display: grid;
173
+ gap: var(--ts-space-1);
174
+ min-width: 0;
175
+ }
176
+
177
+ .ts-app-shell__title h1 {
178
+ color: var(--ts-color-text);
179
+ font-size: 1.55rem;
180
+ line-height: 1.12;
181
+ margin: 0;
182
+ }
183
+
184
+ .ts-app-shell__title p,
185
+ .ts-project-header p {
186
+ color: var(--ts-color-text-muted);
187
+ line-height: 1.5;
188
+ margin: 0;
189
+ }
190
+
191
+ .ts-app-shell__header-actions {
192
+ align-items: center;
193
+ display: flex;
194
+ flex-wrap: wrap;
195
+ gap: var(--ts-space-2);
196
+ justify-content: flex-end;
197
+ }
198
+
199
+ .ts-project-header {
200
+ background: var(--ts-color-surface);
201
+ border: 1px solid var(--ts-color-border);
202
+ border-radius: var(--ts-radius-lg);
203
+ display: grid;
204
+ gap: var(--ts-space-2);
205
+ padding: var(--ts-space-3);
206
+ }
207
+
208
+ .ts-project-header__main {
209
+ display: grid;
210
+ gap: var(--ts-space-2);
211
+ }
212
+
213
+ .ts-project-header__badges,
214
+ .ts-project-header__actions {
215
+ display: flex;
216
+ flex-wrap: wrap;
217
+ gap: var(--ts-space-2);
218
+ }
219
+
220
+ .ts-project-header h2 {
221
+ color: var(--ts-color-text);
222
+ font-size: 1.15rem;
223
+ line-height: 1.15;
224
+ margin: 0;
225
+ }
226
+
227
+ .ts-shell-tabs {
228
+ align-items: stretch;
229
+ display: flex;
230
+ flex-wrap: wrap;
231
+ gap: var(--ts-space-2);
232
+ }
233
+
234
+ .ts-shell-tab {
235
+ align-items: center;
236
+ background: var(--ts-color-surface-muted);
237
+ border: 1px solid var(--ts-color-border);
238
+ border-radius: var(--ts-radius-md);
239
+ box-sizing: border-box;
240
+ display: inline-flex;
241
+ flex: 1 1 7rem;
242
+ font-size: 0.875rem;
243
+ font-weight: 650;
244
+ justify-content: center;
245
+ line-height: 1.15;
246
+ min-height: 2.25rem;
247
+ padding: 0.45rem 0.65rem;
248
+ text-align: center;
249
+ white-space: nowrap;
250
+ }
251
+
252
+ .ts-shell-tab[aria-current='page'],
253
+ .ts-shell-tab[aria-selected='true'],
254
+ .ts-shell-tab:hover {
255
+ background: var(--ts-color-surface);
256
+ border-color: var(--ts-color-border-strong);
257
+ color: var(--ts-color-text);
258
+ text-decoration: none;
259
+ }
260
+
261
+ .ts-bottom-nav {
262
+ background: var(--ts-color-surface-overlay);
263
+ border-top: 1px solid var(--ts-color-border);
264
+ bottom: 0;
265
+ display: none;
266
+ gap: var(--ts-space-1);
267
+ grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
268
+ left: 0;
269
+ padding: var(--ts-space-2);
270
+ position: fixed;
271
+ right: 0;
272
+ z-index: 30;
273
+ }
274
+
275
+ .ts-bottom-nav__link {
276
+ border-radius: var(--ts-radius-md);
277
+ display: grid;
278
+ font-size: 0.75rem;
279
+ font-weight: 700;
280
+ min-height: 2.5rem;
281
+ place-items: center;
282
+ text-align: center;
283
+ }
284
+
285
+ .ts-bottom-nav__link[aria-current='page'],
286
+ .ts-bottom-nav__link:hover {
287
+ background: var(--ts-color-surface-muted);
288
+ color: var(--ts-color-text);
289
+ text-decoration: none;
290
+ }
291
+
292
+ .ts-public-shell {
293
+ background: var(--ts-color-canvas);
294
+ color: var(--ts-color-text);
295
+ margin: 0 auto;
296
+ max-width: var(--ts-shell-width);
297
+ min-height: 100vh;
298
+ padding: var(--ts-space-3);
299
+ }
300
+
301
+ .ts-public-shell__header {
302
+ display: grid;
303
+ gap: var(--ts-space-2);
304
+ padding-bottom: var(--ts-space-3);
305
+ }
306
+
307
+ .ts-public-shell__nav {
308
+ display: flex;
309
+ flex-wrap: wrap;
310
+ gap: var(--ts-space-2);
311
+ }
312
+
313
+ .ts-public-shell__link {
314
+ border: 1px solid transparent;
315
+ border-radius: 999px;
316
+ font-size: 0.94rem;
317
+ font-weight: 650;
318
+ padding: 0.42rem 0.7rem;
319
+ }
320
+
321
+ .ts-public-shell__link[aria-current='page'],
322
+ .ts-public-shell__link:hover {
323
+ background: var(--ts-color-surface-raised);
324
+ border-color: var(--ts-color-border);
325
+ color: var(--ts-color-text);
326
+ text-decoration: none;
327
+ }
328
+
329
+ .ts-public-shell__main {
330
+ display: grid;
331
+ gap: var(--ts-space-3);
332
+ }
333
+
334
+ @media (min-width: 48rem) {
335
+ .ts-project-header {
336
+ align-items: center;
337
+ grid-template-columns: minmax(0, 1fr) auto;
338
+ }
339
+
340
+ .ts-project-header .ts-shell-tabs {
341
+ grid-column: 1 / -1;
342
+ }
343
+ }
344
+
345
+ @media (max-width: 61.25rem) {
346
+ .ts-app-shell {
347
+ display: block;
348
+ padding-bottom: 4.5rem;
349
+ }
350
+
351
+ .ts-app-shell__rail {
352
+ display: none;
353
+ }
354
+
355
+ .ts-app-shell__main {
356
+ padding: var(--ts-space-2);
357
+ }
358
+
359
+ .ts-app-shell__mobile-top {
360
+ background: var(--ts-color-surface-overlay);
361
+ border-bottom: 1px solid var(--ts-color-border);
362
+ display: flex;
363
+ margin: calc(var(--ts-space-2) * -1) calc(var(--ts-space-2) * -1) 0;
364
+ padding: var(--ts-space-2);
365
+ position: sticky;
366
+ top: 0;
367
+ z-index: 20;
368
+ }
369
+
370
+ .ts-app-shell__header {
371
+ flex-direction: column;
372
+ }
373
+
374
+ .ts-app-shell__header-actions {
375
+ justify-content: stretch;
376
+ width: 100%;
377
+ }
378
+
379
+ .ts-bottom-nav {
380
+ display: grid;
381
+ }
382
+ }
383
+
384
+ @media (max-width: 40rem) {
385
+ .ts-public-shell {
386
+ padding: var(--ts-space-2);
387
+ }
388
+
389
+ .ts-top-bar {
390
+ align-items: flex-start;
391
+ flex-direction: column;
392
+ }
393
+
394
+ .ts-top-bar__actions {
395
+ justify-content: flex-start;
396
+ width: 100%;
397
+ }
398
+ }