@tabelhadev/tabelhawebui 0.28.4

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 (175) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +222 -0
  3. package/README.pt-BR.md +217 -0
  4. package/dist/actions/click-outside.d.ts +3 -0
  5. package/dist/actions/click-outside.js +12 -0
  6. package/dist/actions/combobox-keyboard-nav.d.ts +9 -0
  7. package/dist/actions/combobox-keyboard-nav.js +28 -0
  8. package/dist/actions/focus-trap.d.ts +1 -0
  9. package/dist/actions/focus-trap.js +34 -0
  10. package/dist/actions/scroll-lock.d.ts +1 -0
  11. package/dist/actions/scroll-lock.js +17 -0
  12. package/dist/components/actions/Button.svelte +169 -0
  13. package/dist/components/actions/Button.svelte.d.ts +19 -0
  14. package/dist/components/appshell/AppShell.svelte +50 -0
  15. package/dist/components/appshell/AppShell.svelte.d.ts +19 -0
  16. package/dist/components/appshell/AppShellBottomNav.svelte +67 -0
  17. package/dist/components/appshell/AppShellBottomNav.svelte.d.ts +9 -0
  18. package/dist/components/appshell/AppShellContent.svelte +38 -0
  19. package/dist/components/appshell/AppShellContent.svelte.d.ts +10 -0
  20. package/dist/components/appshell/AppShellSidebar.svelte +196 -0
  21. package/dist/components/appshell/AppShellSidebar.svelte.d.ts +15 -0
  22. package/dist/components/appshell/types.d.ts +6 -0
  23. package/dist/components/appshell/types.js +1 -0
  24. package/dist/components/appshell/utils.d.ts +1 -0
  25. package/dist/components/appshell/utils.js +3 -0
  26. package/dist/components/card/Card.svelte +31 -0
  27. package/dist/components/card/Card.svelte.d.ts +9 -0
  28. package/dist/components/card/CardAction.svelte +57 -0
  29. package/dist/components/card/CardAction.svelte.d.ts +10 -0
  30. package/dist/components/card/CardContent.svelte +25 -0
  31. package/dist/components/card/CardContent.svelte.d.ts +8 -0
  32. package/dist/components/card/CardDescription.svelte +24 -0
  33. package/dist/components/card/CardDescription.svelte.d.ts +8 -0
  34. package/dist/components/card/CardFooter.svelte +25 -0
  35. package/dist/components/card/CardFooter.svelte.d.ts +8 -0
  36. package/dist/components/card/CardHeader.svelte +54 -0
  37. package/dist/components/card/CardHeader.svelte.d.ts +10 -0
  38. package/dist/components/card/CardTitle.svelte +25 -0
  39. package/dist/components/card/CardTitle.svelte.d.ts +8 -0
  40. package/dist/components/chat/ChatMessage.svelte +104 -0
  41. package/dist/components/chat/ChatMessage.svelte.d.ts +10 -0
  42. package/dist/components/choice/Listbox.svelte +183 -0
  43. package/dist/components/choice/Listbox.svelte.d.ts +15 -0
  44. package/dist/components/choice/MultiSelect.svelte +278 -0
  45. package/dist/components/choice/MultiSelect.svelte.d.ts +18 -0
  46. package/dist/components/choice/Rating.svelte +91 -0
  47. package/dist/components/choice/Rating.svelte.d.ts +12 -0
  48. package/dist/components/choice/Toggle.svelte +112 -0
  49. package/dist/components/choice/Toggle.svelte.d.ts +12 -0
  50. package/dist/components/feedback/Badge.svelte +66 -0
  51. package/dist/components/feedback/Badge.svelte.d.ts +9 -0
  52. package/dist/components/feedback/ErrorPage.svelte +99 -0
  53. package/dist/components/feedback/ErrorPage.svelte.d.ts +13 -0
  54. package/dist/components/feedback/Message.svelte +130 -0
  55. package/dist/components/feedback/Message.svelte.d.ts +14 -0
  56. package/dist/components/feedback/ProgressBar.svelte +69 -0
  57. package/dist/components/feedback/ProgressBar.svelte.d.ts +8 -0
  58. package/dist/components/feedback/Skeleton.svelte +47 -0
  59. package/dist/components/feedback/Skeleton.svelte.d.ts +9 -0
  60. package/dist/components/feedback/Status.svelte +75 -0
  61. package/dist/components/feedback/Status.svelte.d.ts +10 -0
  62. package/dist/components/feedback/StatusPill.svelte +94 -0
  63. package/dist/components/feedback/StatusPill.svelte.d.ts +13 -0
  64. package/dist/components/feedback/Toaster.svelte +122 -0
  65. package/dist/components/feedback/Toaster.svelte.d.ts +7 -0
  66. package/dist/components/feedback/Tooltip.svelte +126 -0
  67. package/dist/components/feedback/Tooltip.svelte.d.ts +10 -0
  68. package/dist/components/feedback/toast.svelte.d.ts +27 -0
  69. package/dist/components/feedback/toast.svelte.js +23 -0
  70. package/dist/components/forms/Checkbox.svelte +96 -0
  71. package/dist/components/forms/Checkbox.svelte.d.ts +11 -0
  72. package/dist/components/forms/DatePicker.svelte +372 -0
  73. package/dist/components/forms/DatePicker.svelte.d.ts +21 -0
  74. package/dist/components/forms/Field.svelte +47 -0
  75. package/dist/components/forms/Field.svelte.d.ts +11 -0
  76. package/dist/components/forms/FileUpload.svelte +504 -0
  77. package/dist/components/forms/FileUpload.svelte.d.ts +43 -0
  78. package/dist/components/forms/Input.svelte +93 -0
  79. package/dist/components/forms/Input.svelte.d.ts +9 -0
  80. package/dist/components/forms/Label.svelte +30 -0
  81. package/dist/components/forms/Label.svelte.d.ts +10 -0
  82. package/dist/components/forms/Radio.svelte +80 -0
  83. package/dist/components/forms/Radio.svelte.d.ts +11 -0
  84. package/dist/components/forms/Select.svelte +322 -0
  85. package/dist/components/forms/Select.svelte.d.ts +22 -0
  86. package/dist/components/forms/TagInput.svelte +336 -0
  87. package/dist/components/forms/TagInput.svelte.d.ts +16 -0
  88. package/dist/components/forms/Textarea.svelte +64 -0
  89. package/dist/components/forms/Textarea.svelte.d.ts +9 -0
  90. package/dist/components/landing/Landing.svelte +47 -0
  91. package/dist/components/landing/Landing.svelte.d.ts +8 -0
  92. package/dist/components/landing/LandingFeatures.svelte +92 -0
  93. package/dist/components/landing/LandingFeatures.svelte.d.ts +15 -0
  94. package/dist/components/landing/LandingHero.svelte +92 -0
  95. package/dist/components/landing/LandingHero.svelte.d.ts +13 -0
  96. package/dist/components/landing/LandingRoadmap.svelte +42 -0
  97. package/dist/components/landing/LandingRoadmap.svelte.d.ts +11 -0
  98. package/dist/components/landing/LandingSection.svelte +25 -0
  99. package/dist/components/landing/LandingSection.svelte.d.ts +9 -0
  100. package/dist/components/landing/LandingSectionHeading.svelte +19 -0
  101. package/dist/components/landing/LandingSectionHeading.svelte.d.ts +10 -0
  102. package/dist/components/landing/LandingSteps.svelte +100 -0
  103. package/dist/components/landing/LandingSteps.svelte.d.ts +13 -0
  104. package/dist/components/layout/BracketLink.svelte +41 -0
  105. package/dist/components/layout/BracketLink.svelte.d.ts +9 -0
  106. package/dist/components/layout/Divider.svelte +70 -0
  107. package/dist/components/layout/Divider.svelte.d.ts +9 -0
  108. package/dist/components/layout/Eyebrow.svelte +24 -0
  109. package/dist/components/layout/Eyebrow.svelte.d.ts +8 -0
  110. package/dist/components/layout/PageHeader.svelte +106 -0
  111. package/dist/components/layout/PageHeader.svelte.d.ts +15 -0
  112. package/dist/components/layout/PageSeo.svelte +34 -0
  113. package/dist/components/layout/PageSeo.svelte.d.ts +10 -0
  114. package/dist/components/layout/PageShell.svelte +25 -0
  115. package/dist/components/layout/PageShell.svelte.d.ts +8 -0
  116. package/dist/components/layout/Panel.svelte +105 -0
  117. package/dist/components/layout/Panel.svelte.d.ts +12 -0
  118. package/dist/components/layout/Prose.svelte +101 -0
  119. package/dist/components/layout/Prose.svelte.d.ts +7 -0
  120. package/dist/components/layout/RuleCard.svelte +63 -0
  121. package/dist/components/layout/RuleCard.svelte.d.ts +13 -0
  122. package/dist/components/layout/SectionHeading.svelte +57 -0
  123. package/dist/components/layout/SectionHeading.svelte.d.ts +10 -0
  124. package/dist/components/layout/StatTile.svelte +76 -0
  125. package/dist/components/layout/StatTile.svelte.d.ts +12 -0
  126. package/dist/components/layout/TerminalWindow.svelte +81 -0
  127. package/dist/components/layout/TerminalWindow.svelte.d.ts +9 -0
  128. package/dist/components/layout/Wordmark.svelte +26 -0
  129. package/dist/components/layout/Wordmark.svelte.d.ts +8 -0
  130. package/dist/components/list/List.svelte +40 -0
  131. package/dist/components/list/List.svelte.d.ts +13 -0
  132. package/dist/components/list/ListItem.svelte +81 -0
  133. package/dist/components/list/ListItem.svelte.d.ts +13 -0
  134. package/dist/components/marketing/MarketingShell.svelte +170 -0
  135. package/dist/components/marketing/MarketingShell.svelte.d.ts +23 -0
  136. package/dist/components/marketing/MarketingShellFooter.svelte +92 -0
  137. package/dist/components/marketing/MarketingShellFooter.svelte.d.ts +14 -0
  138. package/dist/components/navigation/Accordion.svelte +110 -0
  139. package/dist/components/navigation/Accordion.svelte.d.ts +15 -0
  140. package/dist/components/navigation/Carousel.svelte +115 -0
  141. package/dist/components/navigation/Carousel.svelte.d.ts +12 -0
  142. package/dist/components/navigation/InstructionStep.svelte +115 -0
  143. package/dist/components/navigation/InstructionStep.svelte.d.ts +10 -0
  144. package/dist/components/navigation/InstructionSteps.svelte +39 -0
  145. package/dist/components/navigation/InstructionSteps.svelte.d.ts +8 -0
  146. package/dist/components/navigation/Nav.svelte +102 -0
  147. package/dist/components/navigation/Nav.svelte.d.ts +14 -0
  148. package/dist/components/navigation/Stepper.svelte +112 -0
  149. package/dist/components/navigation/Stepper.svelte.d.ts +12 -0
  150. package/dist/components/navigation/TabCard.svelte +62 -0
  151. package/dist/components/navigation/TabCard.svelte.d.ts +10 -0
  152. package/dist/components/navigation/Tabs.svelte +83 -0
  153. package/dist/components/navigation/Tabs.svelte.d.ts +12 -0
  154. package/dist/components/navigation/ThemeToggle.svelte +118 -0
  155. package/dist/components/navigation/ThemeToggle.svelte.d.ts +20 -0
  156. package/dist/components/overlay/Dialog.svelte +200 -0
  157. package/dist/components/overlay/Dialog.svelte.d.ts +19 -0
  158. package/dist/components/overlay/DialogActions.svelte +50 -0
  159. package/dist/components/overlay/DialogActions.svelte.d.ts +9 -0
  160. package/dist/components/overlay/Dropdown.svelte +110 -0
  161. package/dist/components/overlay/Dropdown.svelte.d.ts +10 -0
  162. package/dist/components/overlay/FloatingActionPill.svelte +96 -0
  163. package/dist/components/overlay/FloatingActionPill.svelte.d.ts +14 -0
  164. package/dist/components/overlay/Sidebar.svelte +258 -0
  165. package/dist/components/overlay/Sidebar.svelte.d.ts +18 -0
  166. package/dist/components/table/Table.svelte +550 -0
  167. package/dist/components/table/Table.svelte.d.ts +42 -0
  168. package/dist/components/timeline/Timeline.svelte +25 -0
  169. package/dist/components/timeline/Timeline.svelte.d.ts +8 -0
  170. package/dist/components/timeline/TimelineItem.svelte +220 -0
  171. package/dist/components/timeline/TimelineItem.svelte.d.ts +18 -0
  172. package/dist/index.d.ts +156 -0
  173. package/dist/index.js +149 -0
  174. package/dist/theme/theme.css +348 -0
  175. package/package.json +52 -0
package/README.md ADDED
@@ -0,0 +1,222 @@
1
+ <div align="center">
2
+
3
+ # TAbelhaWebUI
4
+
5
+ **Shared theme and chrome for ianptkcs's web apps — Catppuccin (Latte/Mocha)
6
+ with a "reading someone's source file" aesthetic: mono carries the structure,
7
+ sharp borders, configurable accent.**
8
+
9
+ **English** · [Português](README.pt-BR.md)
10
+
11
+ [![SvelteKit](https://img.shields.io/badge/SvelteKit-Svelte-ff3e00?style=flat-square&logo=svelte&logoColor=white)](https://kit.svelte.dev)
12
+ [![Cloudflare Workers](https://img.shields.io/badge/Cloudflare-Workers-orange?style=flat-square&logo=cloudflare&logoColor=white)](https://workers.cloudflare.com)
13
+ [![License: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue?style=flat-square)](LICENSE)
14
+
15
+ [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/ianptkcs)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## What it is
22
+
23
+ The web counterpart of [`tabelhatuiui`](https://github.com/TAbelhaDev/tabelhatuiui)
24
+ (the Bubble Tea TUIs): each app keeps only its own business logic, and the
25
+ library takes care of the theme and of the components everyone was redrawing
26
+ from scratch.
27
+
28
+ ## Installation
29
+
30
+ Requires Svelte 5.
31
+
32
+ ```bash
33
+ bun add @tabelhadev/tabelhawebui
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ Import the theme once in the app and use the components:
39
+
40
+ ```svelte
41
+ <script>
42
+ import { Card, Table, Badge, Button, Status } from '@tabelhadev/tabelhawebui';
43
+ </script>
44
+
45
+ <Card>
46
+ <Card.Header>
47
+ <Card.Title>Resumo</Card.Title>
48
+ <Card.Description>mês atual</Card.Description>
49
+ </Card.Header>
50
+ <Card.Content>
51
+ <Status kind="success">configurado</Status>
52
+ <Button variant="primary">+ Nova transação</Button>
53
+ <Table columns={['Data', 'Descrição']} rows={[{ Data: '01/08', Descrição: 'Mercado' }]} />
54
+ </Card.Content>
55
+ </Card>
56
+ ```
57
+
58
+ In the app's CSS (once):
59
+
60
+ ```css
61
+ @import "@tabelhadev/tabelhawebui/theme.css";
62
+ ```
63
+
64
+ ## Components
65
+
66
+ | Component | What it is |
67
+ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
68
+ | `Card` | panel with header and content; composed API `Card.Header`/`Title`/`Description`/`Content`/`Footer` |
69
+ | `TabCard` | "file tab" panel (page chrome) |
70
+ | `Table` | table with `columns`/`rows`, `cell` snippet, sort, global filter, selection, loading/skeleton, empty and pagination (with `pageSizeOptions`; `pageSize={0}` shows every row unpaginated) |
71
+ | `Badge` | small text tag — `default` / `secondary` / `outline` variants |
72
+ | `Button` | `default` / `primary` / `ghost` / `danger` / `outline` variants, `size` (default/sm/lg/icon-sm), `href`, `loading`; `buttonVariants()` |
73
+ | `Panel` | container with `focused` → accent border; `toggleable` collapsible |
74
+ | `Status` | semantic `success` / `warning` / `error` / `info`, with optional `icon` |
75
+ | `Input` | text field (`password`, `file` with `bind:files`, ...), `invalid` |
76
+ | `Textarea` | multi-line text area (`bind:value`, `rows`, `resize: vertical`, `invalid`) |
77
+ | `Label` | 13px mono label |
78
+ | `Field` | form wrapper: label + children (input/textarea/select) + optional error message |
79
+ | `Radio` | single choice: hidden input + circular indicator (accent when checked, focus via `:has()`) |
80
+ | `Checkbox` | multiple choice: hidden input + square with `✓` |
81
+ | `Toggle` | on/off switch (hidden checkbox + `switch` role, `invalid`) |
82
+ | `Select` | custom listbox with `options`, `bind:value`, `name`, keyboard navigation and `filter` |
83
+ | `MultiSelect` | multiple selection in a popover with checkboxes, `filter`, hidden input with `name` |
84
+ | `Listbox` | visible option list (single/multiple), `filter`, `checkmark` |
85
+ | `DatePicker` | date/month picker with a calendar popover, `bind:value`, `min`/`max`, `locale` |
86
+ | `FileUpload` | file picker with drag-and-drop (`basic`/`advanced`), `accept`/`maxFileSize`/`fileLimit` validation, per-file progress, `customUpload` + `uploadHandler` (no built-in network), snippets |
87
+ | `TagInput` | tag/etiqueta field: removable chips, autocomplete/filter of `options`, create-on-the-fly (Enter or create action), Backspace removes the last chip |
88
+ | `Toaster` | renders toasts + the `toast` store; configurable `position` |
89
+ | `Timeline` | timeline (`Timeline.Item`s with rail, dot, skills) |
90
+ | `RuleCard` | card with a top border, icon and serif prose |
91
+ | `Nav` | navbar: logo + breadcrumb links separated by `/` |
92
+ | `ThemeToggle` | toggles `data-theme`/`.dark` + `localStorage`; optional `showLabel` for a visible light/dark label |
93
+ | `AppShell` | authenticated app shell as one compound export: `AppShell.Sidebar` (desktop: brand + nav + theme/logout), `AppShell.BottomNav` (mobile), `AppShell.Content` (`max-w-6xl`) |
94
+ | `Wordmark` | `TAbelha` in text color + `suffix` in accent; inherits the context font/size |
95
+ | `Dropdown` | menu with a printed shadow; closes on outside click/Esc |
96
+ | `Divider` | horizontal/vertical line, `solid`/`dashed`, centered label |
97
+ | `Eyebrow` | uppercase mono section label |
98
+ | `BracketLink` | mono `[ label ]` link, brackets light up on hover |
99
+ | `Message` | inline `info`/`success`/`warn`/`error` alert, `closable`, `life` (auto-dismiss) |
100
+ | `ProgressBar` | determinate/indeterminate progress bar with a label |
101
+ | `ErrorPage` | error page display (`status` in mono/accent, `title`, `description`, `homeHref`/`homeLabel`, `actions` snippet) — used inside `+error.svelte` |
102
+ | `Skeleton` | shimmer placeholder (`width`/`height`/`rounded`) |
103
+ | `Tooltip` | CSS tooltip via `data-tooltip` (`top`/`bottom`/`left`/`right`) |
104
+ | `Dialog` | modal with overlay, `aria-modal`, Esc/outside to close, `footer` snippet, `animate` prop for enter/exit transitions |
105
+ | `Sidebar` | side/`top`/`bottom` panel; `mode` overlay (drawer) or `push` (static, desktop), `collapsible`+`collapsed`, `overlay` off, breakpoint 768 (becomes a full-width drawer on mobile), animated; focus trap + scroll-lock in overlay/mobile mode |
106
+ | `Tabs` | tab bar with bindable `value`, `disabled`, keyboard navigation |
107
+ | `Accordion` | collapsible panels (`multiple`, bindable `value`) |
108
+ | `Stepper` | step indicator (onboarding) with bindable `value` |
109
+ | `Carousel` | carousel with bindable `page`, dots and arrows |
110
+ | `Rating` | stars (`max`, `readonly`, `cancel`) |
111
+ | `StatusPill` | fixed toast-style pill (`position`, `closable`, bindable `visible`) |
112
+ | `FloatingActionPill` | clickable floating pill (`position`, `label`, `expanded` → `aria-expanded`, `animate` for scale+fade transition) — StatusPill's styling, but a `<button>` that opens a panel/dialog |
113
+ | `ChatMessage` | chat bubble (`role` user/assistant, `content`, `name`, `streaming` → pulsing ellipsis) — presentational render, no SSE logic |
114
+ | `Landing` | landing sections as one compound export: `Landing.Hero`, `Landing.Steps` (numbered cards), `Landing.Features`, `Landing.Roadmap`, `Landing.Footer` (`name` + optional `tagline`/`license`/repo link); `Hero`/`Steps`/`Features` take `justify` for justified serif prose |
115
+ | `SectionHeading` | section title: eyebrow + h2 + lead (optional `justify` for justified serif lead) |
116
+ | `TerminalWindow` | terminal window: dot bar + `title` + content (`children`) |
117
+ | `PageHeader` | page header: `title`, optional `subtitle` (with `//` prefix), optional `action` snippet on the right side |
118
+ | `StatTile` | stat card: `label`, `value`, optional `href` (label becomes link), `valueClass` for color, `footer` snippet for delta/sub-label |
119
+
120
+ ## Tokens
121
+
122
+ The theme exposes `--twui-*` custom properties:
123
+
124
+ - Semantic: `--twui-paper`, `--twui-paper-raised`, `--twui-ink`,
125
+ `--twui-ink-soft`, `--twui-ink-faint`, `--twui-rule`, `--twui-accent`,
126
+ `--twui-accent-soft`, `--twui-signal`, `--twui-signal-soft`, `--twui-danger`
127
+ - Fonts: `--twui-font-mono`, `--twui-font-serif`
128
+ - Shadow: `--twui-shadow-offset` (hard 3px offset)
129
+ - Palette for the active flavour: `--twui-base`, `--twui-surface0`,
130
+ `--twui-text`, `--twui-red`, `--twui-green`, `--twui-mauve`, ... (via
131
+ `--twui-<color>`)
132
+ - Raw colours per flavour: `--twui-latte-*` and `--twui-mocha-*` (the full scale
133
+ — neutrals + Catppuccin's 14 accents)
134
+
135
+ Dark theme via `[data-theme="dark"]` **or** a `.dark` class on `<html>`. The
136
+ theme applies `body` defaults (background, colour, serif font) and respects
137
+ `prefers-reduced-motion`.
138
+
139
+ ## Per-app custom accent
140
+
141
+ The default accent is maroon (light) / pink (dark). Each app can have its own by
142
+ **picking a name** through `data-accent` on `<html>` — the library resolves the
143
+ Latte/Mocha pair automatically:
144
+
145
+ ```html
146
+ <html data-accent="teal"></html>
147
+ ```
148
+
149
+ `--twui-accent-soft` derives on its own (via `color-mix`); the semantic
150
+ `--twui-signal`/`--twui-danger` and the neutrals do not change. Available
151
+ accents:
152
+
153
+ | Accent | Latte (light) | Mocha (dark) |
154
+ | ------------------ | ------------- | ------------ |
155
+ | `maroon` (default) | `#e64553` | `#eba0ac` |
156
+ | `mauve` | `#8839ef` | `#cba6f7` |
157
+ | `pink` | `#ea76cb` | `#f5c2e7` |
158
+ | `red` | `#d20f39` | `#f38ba8` |
159
+ | `peach` | `#fe640b` | `#fab387` |
160
+ | `yellow` | `#df8e1d` | `#f9e2af` |
161
+ | `green` | `#40a02b` | `#a6e3a1` |
162
+ | `teal` | `#179299` | `#94e2d5` |
163
+ | `lavender` | `#7287fd` | `#b4befe` |
164
+ | `rosewater` | `#dc8a78` | `#f5e0dc` |
165
+ | `flamingo` | `#dd7878` | `#f2cdcd` |
166
+ | `blue` | `#1e66f5` | `#89b4fa` |
167
+ | `sky` | `#04a5e5` | `#89dceb` |
168
+ | `sapphire` | `#209fb5` | `#74c7ec` |
169
+
170
+ (Escape hatch: anyone needing a colour outside the list can override
171
+ `--twui-accent` directly in `layout.css`, in both themes.)
172
+
173
+ ## Development
174
+
175
+ Stack: Svelte 5 + SvelteKit, Bun as the package manager.
176
+
177
+ ```sh
178
+ bun install
179
+ bun run check # typecheck (svelte-kit sync + svelte-check)
180
+ bun run lint # prettier
181
+ bun run build # svelte-package (dist)
182
+ ```
183
+
184
+ The package is published to npm as `@tabelhadev/tabelhawebui`; versions follow
185
+ `CHANGELOG.md` and the convention in `CONTRIBUTING.md`.
186
+
187
+ ## Tooling
188
+
189
+ - **MCP server** (`mcp/`): exposes components, tokens and accents as MCP tools
190
+ for coding agents (props with types, light/dark `--twui-*`, `data-accent`).
191
+ See `mcp/README.md` to install and run it.
192
+
193
+ ## Requesting a component
194
+
195
+ Feature requests from other agents live in `requests/` as markdown files — see
196
+ `requests/README.md` for the flow, and `requests/_template.md` for what to fill
197
+ in. From outside this repo, open a feature request issue instead.
198
+
199
+ ## Changelog
200
+
201
+ See [CHANGELOG.md](CHANGELOG.md) for the version history.
202
+
203
+ ## Support the project
204
+
205
+ - **Global**: [ko-fi.com/ianptkcs](https://ko-fi.com/ianptkcs)
206
+ - **Brazil (Pix)**: scan the QR below or copy the code
207
+
208
+ <img src="pix-qr.png" alt="Pix QR" width="200" />
209
+
210
+ <details><summary>Pix code (copy)</summary>
211
+
212
+ ```
213
+ 00020126580014BR.GOV.BCB.PIX01365ad933b0-dcdc-4525-a736-0759902aeec65204000053039865802BR5925Ian Patrick da Costa Soar6009SAO PAULO62140510tQA85x6Dov63041FB6
214
+ ```
215
+
216
+ </details>
217
+
218
+ ## License
219
+
220
+ [AGPL-3.0](LICENSE) — strong copyleft: you may use, modify and even host
221
+ TAbelhaWebUI commercially, but any modified version, including one running as a
222
+ network service (SaaS), has to stay open source under the same license.
@@ -0,0 +1,217 @@
1
+ <div align="center">
2
+
3
+ # TAbelhaWebUI
4
+
5
+ **Tema + chrome compartilhado dos apps web do ianptkcs — Catppuccin (Latte/Mocha)
6
+ com a estética "reading someone's source file": mono leva a estrutura, bordas
7
+ afiadas, acento configurável.**
8
+
9
+ [English](README.md) · **Português**
10
+
11
+ [![SvelteKit](https://img.shields.io/badge/SvelteKit-Svelte-ff3e00?style=flat-square&logo=svelte&logoColor=white)](https://kit.svelte.dev)
12
+ [![Cloudflare Workers](https://img.shields.io/badge/Cloudflare-Workers-orange?style=flat-square&logo=cloudflare&logoColor=white)](https://workers.cloudflare.com)
13
+ [![License: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-blue?style=flat-square)](LICENSE)
14
+
15
+ [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/ianptkcs)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## O que é
22
+
23
+ Equivalente web do [`tabelhatuiui`](https://github.com/TAbelhaDev/tabelhatuiui)
24
+ (os TUIs Bubble Tea): cada app mantém só o negócio dele; a lib cuida do tema e
25
+ dos componentes que todo mundo desenhava do zero.
26
+
27
+ ## Instalação
28
+
29
+ Requer Svelte 5.
30
+
31
+ ```bash
32
+ bun add @tabelhadev/tabelhawebui
33
+ ```
34
+
35
+ ## Uso
36
+
37
+ Importe o tema uma vez no app e use os componentes:
38
+
39
+ ```svelte
40
+ <script>
41
+ import { Card, Table, Badge, Button, Status } from '@tabelhadev/tabelhawebui';
42
+ </script>
43
+
44
+ <Card>
45
+ <Card.Header>
46
+ <Card.Title>Resumo</Card.Title>
47
+ <Card.Description>mês atual</Card.Description>
48
+ </Card.Header>
49
+ <Card.Content>
50
+ <Status kind="success">configurado</Status>
51
+ <Button variant="primary">+ Nova transação</Button>
52
+ <Table columns={['Data', 'Descrição']} rows={[{ Data: '01/08', Descrição: 'Mercado' }]} />
53
+ </Card.Content>
54
+ </Card>
55
+ ```
56
+
57
+ No CSS do app (uma vez):
58
+
59
+ ```css
60
+ @import "@tabelhadev/tabelhawebui/theme.css";
61
+ ```
62
+
63
+ ## Componentes
64
+
65
+ | Componente | O que é |
66
+ | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
67
+ | `Card` | painel com header e conteúdo; API composta `Card.Header`/`Title`/`Description`/`Content`/`Footer` |
68
+ | `TabCard` | painel "aba de arquivo" (chrome de página) |
69
+ | `Table` | tabelha com `columns`/`rows`, snippet `cell`, sort, filtro global, selection, loading/skeleton, empty e paginação (com `pageSizeOptions`; `pageSize={0}` mostra todas as linhas sem paginação) |
70
+ | `Badge` | tag de texto pequena — variantes `default` / `secondary` / `outline` |
71
+ | `Button` | variantes `default` / `primary` / `ghost` / `danger` / `outline`, `size` (default/sm/lg/icon-sm), `href`, `loading`; `buttonVariants()` |
72
+ | `Panel` | contêiner com `focused` → borda accent; `toggleable` colapsável |
73
+ | `Status` | semântico `success` / `warning` / `error` / `info`, com `icon` opcional |
74
+ | `Input` | campo de texto (`password`, `file` com `bind:files`, ...), `invalid` |
75
+ | `Textarea` | área de texto multi-linha (`bind:value`, `rows`, `resize: vertical`, `invalid`) |
76
+ | `Label` | rótulo mono 13px |
77
+ | `Field` | wrapper de form: label + children (input/textarea/select) + mensagem de erro opcional |
78
+ | `Radio` | opção única: input oculto + indicador circular (accent no checked, foco por `:has()`) |
79
+ | `Checkbox` | opção múltipla: input oculto + quadrado com `✓` |
80
+ | `Toggle` | switch on/off (hidden checkbox + role `switch`, `invalid`) |
81
+ | `Select` | listbox custom com `options`, `bind:value`, `name`, navegação por teclado e `filter` |
82
+ | `MultiSelect` | seleção múltipla em popover com checkboxes, `filter`, hidden input com `name` |
83
+ | `Listbox` | lista de opções visível (single/múltipla), `filter`, `checkmark` |
84
+ | `DatePicker` | seletor de data/mês com popover calendário, `bind:value`, `min`/`max`, `locale` |
85
+ | `FileUpload` | seletor de arquivos com drag-and-drop (`basic`/`advanced`), validação de `accept`/`maxFileSize`/`fileLimit`, progresso por arquivo, `customUpload` + `uploadHandler` (sem rede embutida), snippets |
86
+ | `Toaster` | renderiza toasts + store `toast`; `position` configurável |
87
+ | `Timeline` | linha do tempo (`Timeline.Item`s com trilho, dot, skills) |
88
+ | `RuleCard` | cartão com borda-superior, ícone e prosa serif |
89
+ | `Nav` | navbar: logo + links em breadcrumb com `/` |
90
+ | `ThemeToggle` | alterna `data-theme`/`.dark` + `localStorage`; `showLabel` opcional pra label visível claro/escuro |
91
+ | `AppShell` | shell do app autenticado num só export composto: `AppShell.Sidebar` (desktop: brand + nav + tema/logout), `AppShell.BottomNav` (mobile), `AppShell.Content` (`max-w-6xl`) |
92
+ | `Wordmark` | `TAbelha` na cor do texto + `suffix` em accent; herda a fonte/tamanho do contexto |
93
+ | `Dropdown` | menu com sombra impressa; fecha por fora/Esc |
94
+ | `Divider` | linha horizontal/vertical, `solid`/`dashed`, label central |
95
+ | `Eyebrow` | label de seção mono uppercase |
96
+ | `BracketLink` | link mono `[ label ]`, colchetes acendem no hover |
97
+ | `Message` | alerta inline `info`/`success`/`warn`/`error`, `closable`, `life` (auto-dismiss) |
98
+ | `ProgressBar` | barra de progresso determinada/indeterminada com label |
99
+ | `ErrorPage` | display de página de erro (`status` em mono/accent, `title`, `description`, `homeHref`/`homeLabel`, `actions` snippet) — usado dentro de `+error.svelte` |
100
+ | `Skeleton` | shimmer placeholder (`width`/`height`/`rounded`) |
101
+ | `Tooltip` | tooltip CSS via `data-tooltip` (`top`/`bottom`/`left`/`right`) |
102
+ | `Dialog` | modal com overlay, `aria-modal`, Esc/outside pra fechar, `footer` snippet |
103
+ | `Sidebar` | painel lateral/`top`/`bottom`; `mode` overlay (drawer) ou `push` (estático, desktop), `collapsible`+`collapsed`, `overlay` off, breakpoint 768 (mobile vira drawer full-width), animado |
104
+ | `Tabs` | barra de abas com `value` bindable, `disabled`, navegação por teclado |
105
+ | `Accordion` | painéis colapsáveis (`multiple`, `value` bindable) |
106
+ | `Stepper` | indicador de passos (onboarding) com `value` bindable |
107
+ | `Carousel` | carrossel com `page` bindable, dots e arrows |
108
+ | `Rating` | estrelas (`max`, `readonly`, `cancel`) |
109
+ | `StatusPill` | pill fixa estilo toast (`position`, `closable`, `visible` bindable) |
110
+ | `FloatingActionPill` | pill flutuante clicável (`position`, `label`, `expanded` → `aria-expanded`) — estilo do StatusPill, mas `<button>` que abre painel/dialog |
111
+ | `ChatMessage` | bubble de chat (`role` user/assistant, `content`, `name`, `streaming` → ellipsis pulsando) — render presentacional, sem lógica de SSE |
112
+ | `Landing` | seções de landing num só export composto: `Landing.Hero`, `Landing.Steps` (cards numerados, número em box colorido), `Landing.Features`, `Landing.Roadmap`, `Landing.Footer` (`name` + `tagline`/licença/link de repo opcionais) |
113
+ | `SectionHeading` | título de seção: eyebrow + h2 + lead |
114
+ | `TerminalWindow` | janela de terminal: barra de dots + `title` + conteúdo (`children`) |
115
+
116
+ ## Tokens
117
+
118
+ O tema expõe custom properties `--twui-*`:
119
+
120
+ - Semânticos: `--twui-paper`, `--twui-paper-raised`, `--twui-ink`,
121
+ `--twui-ink-soft`, `--twui-ink-faint`, `--twui-rule`, `--twui-accent`,
122
+ `--twui-accent-soft`, `--twui-signal`, `--twui-signal-soft`, `--twui-danger`
123
+ - Fontes: `--twui-font-mono`, `--twui-font-serif`
124
+ - Sombra: `--twui-shadow-offset` (hard offset 3px)
125
+ - Paleta por flavor ativo: `--twui-base`, `--twui-surface0`, `--twui-text`,
126
+ `--twui-red`, `--twui-green`, `--twui-mauve`, ... (via `--twui-<color>`)
127
+ - Cores cruas por flavor: `--twui-latte-*` e `--twui-mocha-*` (escala completa
128
+ — neutros + 14 acentos do Catppuccin)
129
+
130
+ Tema escuro via `[data-theme="dark"]` **ou** classe `.dark` no `<html>`. O
131
+ tema aplica defaults de `body` (fundo, cor, fonte serif) e respeita
132
+ `prefers-reduced-motion`.
133
+
134
+ ## Accent custom por app
135
+
136
+ O accent padrão é o marrom (claro) / pink (escuro). Cada app pode ter o seu
137
+ **escolhendo um nome** via `data-accent` no `<html>` — a lib resolve o par
138
+ Latte/Mocha automaticamente:
139
+
140
+ ```html
141
+ <html data-accent="teal"></html>
142
+ ```
143
+
144
+ O `--twui-accent-soft` deriva sozinho (via `color-mix`); os semânticos
145
+ `--twui-signal`/`--twui-danger` e os neutros não mudam. Acentos disponíveis:
146
+
147
+ | Accent | Latte (claro) | Mocha (escuro) |
148
+ | ------------------ | ------------- | -------------- |
149
+ | `maroon` (default) | `#e64553` | `#eba0ac` |
150
+ | `mauve` | `#8839ef` | `#cba6f7` |
151
+ | `pink` | `#ea76cb` | `#f5c2e7` |
152
+ | `red` | `#d20f39` | `#f38ba8` |
153
+ | `peach` | `#fe640b` | `#fab387` |
154
+ | `yellow` | `#df8e1d` | `#f9e2af` |
155
+ | `green` | `#40a02b` | `#a6e3a1` |
156
+ | `teal` | `#179299` | `#94e2d5` |
157
+ | `lavender` | `#7287fd` | `#b4befe` |
158
+ | `rosewater` | `#dc8a78` | `#f5e0dc` |
159
+ | `flamingo` | `#dd7878` | `#f2cdcd` |
160
+ | `blue` | `#1e66f5` | `#89b4fa` |
161
+ | `sky` | `#04a5e5` | `#89dceb` |
162
+ | `sapphire` | `#209fb5` | `#74c7ec` |
163
+
164
+ (Escape hatch: quem precisar de uma cor fora da lista pode sobrescrever
165
+ `--twui-accent` direto no `layout.css`, nos dois temas.)
166
+
167
+ ## Desenvolvimento
168
+
169
+ Stack: Svelte 5 + SvelteKit, Bun como package manager.
170
+
171
+ ```sh
172
+ bun install
173
+ bun run check # typecheck (svelte-kit sync + svelte-check)
174
+ bun run lint # prettier
175
+ bun run build # svelte-package (dist)
176
+ ```
177
+
178
+ O pacote é publicado no npm como `@tabelhadev/tabelhawebui`; as versões seguem o
179
+ `CHANGELOG.md` e a convenção do `CONTRIBUTING.md`.
180
+
181
+ ## Ferramentas
182
+
183
+ - **MCP server** (`mcp/`): expõe componentes, tokens e accents como tools
184
+ MCP pra coding agents (props com tipos, `--twui-*` light/dark,
185
+ `data-accent`). Veja `mcp/README.md` pra instalar/rodar.
186
+
187
+ ## Pedindo um componente
188
+
189
+ Requests de feature feitos por outros agents vivem em `requests/` como arquivos
190
+ markdown — veja `requests/README.md` pro fluxo, e `requests/_template.md` pro
191
+ que preencher. De fora deste repo, abra uma issue de feature request.
192
+
193
+ ## Changelog
194
+
195
+ Veja [CHANGELOG.md](CHANGELOG.md) para o histórico de versões.
196
+
197
+ ## Apoie o projeto
198
+
199
+ - **Global**: [ko-fi.com/ianptkcs](https://ko-fi.com/ianptkcs)
200
+ - **Brasil (Pix)**: escaneie o QR abaixo ou copie o código
201
+
202
+ <img src="pix-qr.png" alt="Pix QR" width="200" />
203
+
204
+ <details><summary>Código Pix (copiar)</summary>
205
+
206
+ ```
207
+ 00020126580014BR.GOV.BCB.PIX01365ad933b0-dcdc-4525-a736-0759902aeec65204000053039865802BR5925Ian Patrick da Costa Soar6009SAO PAULO62140510tQA85x6Dov63041FB6
208
+ ```
209
+
210
+ </details>
211
+
212
+ ## Licença
213
+
214
+ [AGPL-3.0](LICENSE) — copyleft forte: você pode usar, modificar e até
215
+ hospedar o TAbelhaWebUI comercialmente, mas qualquer versão modificada,
216
+ inclusive rodando como serviço via rede (SaaS), precisa continuar open source
217
+ sob a mesma licença.
@@ -0,0 +1,3 @@
1
+ export declare function clickOutside(node: HTMLElement, callback: () => void): {
2
+ destroy: () => void;
3
+ };
@@ -0,0 +1,12 @@
1
+ // Shared Svelte action: fires `callback` on pointerdown outside the node.
2
+ // Used by Dropdown, Select, TagInput, DatePicker, MultiSelect.
3
+ export function clickOutside(node, callback) {
4
+ function handler(e) {
5
+ if (!node.contains(e.target))
6
+ callback();
7
+ }
8
+ document.addEventListener("pointerdown", handler);
9
+ return {
10
+ destroy: () => document.removeEventListener("pointerdown", handler),
11
+ };
12
+ }
@@ -0,0 +1,9 @@
1
+ export declare function comboboxKeydown(params: {
2
+ open: boolean;
3
+ activeIndex: number;
4
+ itemCount: number;
5
+ onActiveIndexChange: (i: number) => void;
6
+ onClose: () => void;
7
+ onConfirm: (index: number) => void;
8
+ onOpen?: () => void;
9
+ }): (e: KeyboardEvent) => void;
@@ -0,0 +1,28 @@
1
+ // Shared combobox keyboard navigation: ArrowUp/Down move an active index,
2
+ // Enter confirms the active item, Escape closes. Used by Select and TagInput.
3
+ export function comboboxKeydown(params) {
4
+ return (e) => {
5
+ if (e.key === "Escape") {
6
+ params.onClose();
7
+ return;
8
+ }
9
+ if (e.key === "ArrowDown") {
10
+ e.preventDefault();
11
+ if (!params.open && params.onOpen) {
12
+ params.onOpen();
13
+ return;
14
+ }
15
+ params.onActiveIndexChange(Math.min(params.activeIndex + 1, params.itemCount - 1));
16
+ return;
17
+ }
18
+ if (e.key === "ArrowUp") {
19
+ e.preventDefault();
20
+ params.onActiveIndexChange(Math.max(params.activeIndex - 1, 0));
21
+ return;
22
+ }
23
+ if (e.key === "Enter" && params.open && params.activeIndex >= 0) {
24
+ e.preventDefault();
25
+ params.onConfirm(params.activeIndex);
26
+ }
27
+ };
28
+ }
@@ -0,0 +1 @@
1
+ export declare function trapFocus(panelEl: HTMLElement, onClose: () => void): () => void;
@@ -0,0 +1,34 @@
1
+ // Shared focus-trap: cycles Tab/Shift+Tab inside `panelEl`, matching the
2
+ // behaviour already in Dialog.svelte (WCAG 2.4.3). Used by Dialog and Sidebar
3
+ // (when in overlay / mobile modal mode).
4
+ import { tick } from "svelte";
5
+ const FOCUSABLE = 'a[href], button:not([disabled]), input, select, textarea, [tabindex]:not([tabindex="-1"])';
6
+ export function trapFocus(panelEl, onClose) {
7
+ const onKey = (e) => {
8
+ if (e.key === "Escape") {
9
+ onClose();
10
+ return;
11
+ }
12
+ if (e.key !== "Tab")
13
+ return;
14
+ const focusable = Array.from(panelEl.querySelectorAll(FOCUSABLE));
15
+ if (focusable.length === 0)
16
+ return;
17
+ const first = focusable[0];
18
+ const last = focusable[focusable.length - 1];
19
+ if (e.shiftKey && document.activeElement === first) {
20
+ e.preventDefault();
21
+ last.focus();
22
+ }
23
+ else if (!e.shiftKey && document.activeElement === last) {
24
+ e.preventDefault();
25
+ first.focus();
26
+ }
27
+ };
28
+ document.addEventListener("keydown", onKey);
29
+ tick().then(() => {
30
+ const first = panelEl?.querySelector(FOCUSABLE);
31
+ (first ?? panelEl)?.focus();
32
+ });
33
+ return () => document.removeEventListener("keydown", onKey);
34
+ }
@@ -0,0 +1 @@
1
+ export declare function lockScroll(): () => void;
@@ -0,0 +1,17 @@
1
+ // Shared scroll-lock counter: document.body.style.overflow is locked while ANY
2
+ // consumer (Dialog, Sidebar overlay, …) is open, and only restored when the
3
+ // last one closes. This prevents the bug where closing one modal unlocks scroll
4
+ // while another is still visible.
5
+ let openCount = 0;
6
+ export function lockScroll() {
7
+ if (openCount === 0) {
8
+ document.body.style.overflow = "hidden";
9
+ }
10
+ openCount += 1;
11
+ return () => {
12
+ openCount -= 1;
13
+ if (openCount === 0) {
14
+ document.body.style.overflow = "";
15
+ }
16
+ };
17
+ }