@ekkolyth/ui 0.1.18 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,232 +1,121 @@
1
1
  # @ekkolyth/ui
2
2
 
3
- Shared UI component library, published as `@ekkolyth/ui`.
3
+ Shared UI component library.
4
4
 
5
5
  ## Installation
6
6
 
7
7
  ```sh
8
- bun add @ekkolyth/ui
9
- ```
10
-
11
- ## Peer Dependencies
12
-
13
- - `react` ^19.2.0
14
- - `react-dom` ^19.2.0
15
-
16
- `next` is only reachable through the `/widget` compatibility entry
17
- (`Widget`). It ships as an ordinary workspace dependency here; the
18
- published package declares it as an **optional** peer so an app that never
19
- imports `/widget` never installs it.
20
-
21
- ## Exports
22
-
23
- | Subpath | What it is |
24
- |---|---|
25
- | `.` | All UI components, hooks, and utilities. Theme runtime is not here — see `/themes/provider`. |
26
- | `./text` | `RichText` — renders Tiptap `JSONContent` through a node registry. |
27
- | `./layout` | `Container`. |
28
- | `./blocks` | `ColorPicker`, `Field`, `IconPicker`, `LoadingScreen`, `Widget`. |
29
- | `./hooks` | `useIsMobile`. |
30
- | `./themes/provider` | The framework-neutral theme runtime — see below. |
31
- | `./themes` | Stylesheet: all seven palettes, `@import`ed. |
32
- | `./themes/catpuccin/latte`, `./themes/catpuccin/frappe`, `./themes/catpuccin/macchiato`, `./themes/catpuccin/mocha`, `./themes/tokyo-night`, `./themes/ekko-os`, `./themes/ekkolyth` | Individual palette stylesheets — seven subpaths, one per palette. |
33
- | `./base.css` | The base theme layer — see "Base CSS" below. |
34
-
35
- `./widget` and `./convex` are **compatibility entries, not documented API.**
36
- They exist because specific `apps/web` call sites import from them and sit
37
- outside this package's edit surface; don't build new code against them.
38
-
39
- **Components:**
40
- Built on top of shadcn@latest.
41
-
42
- ```tsx
43
- import { Button, Card, Dialog, useIsMobile, cn } from '@ekkolyth/ui'
44
- ```
45
-
46
- ## Theme Provider
47
-
48
- One framework-neutral provider, at `@ekkolyth/ui/themes/provider`. No
49
- `next-themes` — a vitest guard (`src/index.test.ts`) fails the build if
50
- anything reachable from the root barrel imports it. The subpath exports:
51
-
52
- ```tsx
53
- import {
54
- ThemeProvider,
55
- ThemeScript,
56
- ThemeSelect,
57
- ThemeToggle,
58
- Toaster,
59
- useTheme,
60
- } from '@ekkolyth/ui/themes/provider'
61
- import type {
62
- Appearance,
63
- DarkThemes,
64
- LightThemes,
65
- ThemeContextState,
66
- ThemeName,
67
- ThemeProviderProps,
68
- ThemeScriptProps,
69
- } from '@ekkolyth/ui/themes/provider'
70
- ```
71
-
72
- **`ThemeProviderProps`:**
73
- - `defaultAppearance?: Appearance` — `"light" | "dark" | "system" | "normal"`, default `"system"`. `"normal"` is a legacy alias for `"light"`.
74
- - `lightTheme?: LightThemes | LightThemes[]` — light palette(s) to make available, default `"ekkolyth-light"`.
75
- - `darkTheme?: DarkThemes | DarkThemes[]` — dark palette(s) to make available, default `"ekkolyth-dark"`.
76
- - `storageKey?: string` — localStorage key prefix, default `"ekko-ui"`. Most apps won't need this; it matters if several frontends share a domain and need independent theme state.
77
-
78
- **`useTheme()` returns `ThemeContextState`:**
79
- - `appearance: Appearance` — the mode the user picked, before resolving `"system"`.
80
- - `resolvedMode: 'light' | 'dark'` — `appearance` resolved against `matchMedia` when it's `"system"`.
81
- - `resolvedTheme: ThemeName` — the palette currently applied for `resolvedMode`.
82
- - `availableThemes: { light: LightThemes[], dark: DarkThemes[] }`
83
- - `setAppearance: (appearance: Appearance) => void`
84
- - `setSpecificTheme: (mode: 'light' | 'dark', theme: LightThemes | DarkThemes) => void`
85
-
86
- `ThemeName` is the union of every palette (`LightThemes | DarkThemes`) —
87
- which palette is showing. `Appearance` is a separate concept — light/dark/system
88
- preference — deliberately a different word so the two don't get conflated at a
89
- call site.
90
-
91
- **`ThemeName` values:**
92
- - `"latte" | "ekkoos-light" | "ekkolyth-light"` — light palettes
93
- - `"mocha" | "frappe" | "macchiato" | "tokyo-night" | "ekkoos-dark" | "ekkolyth-dark"` — dark palettes
94
-
95
- ### `ThemeScript`
96
-
97
- Renders a `<script>` that reads the stored appearance/palette from
98
- `localStorage` and sets `data-theme` + the `.dark` class on `<html>` before
99
- React hydrates. Put it in `<head>` — `app/layout.tsx` on Next.js,
100
- `__root.tsx` on TanStack Start — with the same `defaultAppearance`,
101
- `lightTheme`, `darkTheme`, and `storageKey` as the `<ThemeProvider>` it pairs
102
- with. Skipping it doesn't break anything, it just costs one painted frame of
103
- the wrong palette before the provider's own mount effect catches up.
104
-
105
- ```tsx
106
- import { ThemeScript } from '@ekkolyth/ui/themes/provider'
107
-
108
- <head>
109
- <ThemeScript defaultAppearance='system' />
110
- </head>
111
- ```
112
-
113
- ### Theme Toggle / Theme Select
114
-
115
- ```tsx
116
- import { ThemeToggle, ThemeSelect } from '@ekkolyth/ui/themes/provider'
117
- ```
118
-
119
- ## Base CSS
120
-
121
- `@ekkolyth/ui/base.css` is the base theme layer — 34 color tokens with
122
- colourless (grayscale) defaults, the `@theme inline` mapping from those
123
- tokens into Tailwind utilities, `@custom-variant dark`, and an `@layer base`
124
- reset (`* { border-border outline-ring/50 }`, `body { bg-background
125
- text-foreground }`). Import it after the consuming app's own
126
- `@import 'tailwindcss'` and before its palette overrides:
127
-
128
- ```css
129
- @import 'tailwindcss';
130
- @import '@ekkolyth/ui/base.css';
131
- /* app-specific palette overrides go here */
132
- ```
133
-
134
- The export resolves to `./src/base.css`, not `./dist/base.css`, deliberately:
135
- `dist/` is gitignored, so a `dist` path would leave fresh clones and git
136
- worktrees unstyled until someone ran the UI package's build.
137
-
138
- ## Theme Styles
139
-
140
- `@ekkolyth/ui/themes` is a **stylesheet entry** (a CSS `@import`, not a JS
141
- module) that pulls in all seven palettes. It stands in for the app's own
142
- Tailwind preamble, not the other way around — a theme picker that lets the
143
- user switch palettes needs every palette present, so import it alongside the
144
- three lines every consumer already writes:
145
-
146
- ```css
147
- @import 'tailwindcss';
148
- @import 'tw-animate-css';
149
- @import '@ekkolyth/ui/base.css';
150
- @import '@ekkolyth/ui/themes';
151
- ```
8
+ # inside this monorepo
9
+ "@ekkolyth/ui": "workspace:@ekkolyth/ui@*"
152
10
 
153
- Each palette is scoped to `html[data-theme='<name>']`; the provider and
154
- `ThemeScript` switch palettes by setting that attribute, so nothing here is a
155
- runtime import. Pull a single palette instead of the aggregate when an app
156
- only ever shows one:
157
-
158
- ```css
159
- @import '@ekkolyth/ui/themes/catpuccin/latte';
160
- @import '@ekkolyth/ui/themes/tokyo-night';
161
- @import '@ekkolyth/ui/themes/ekkolyth';
11
+ # anywhere else
12
+ bun add @ekkolyth/ui
162
13
  ```
163
14
 
164
- ## Usage Example
15
+ App code MUST always import through its local package alias (`@ekkolyth/ui`),
16
+ never via `@ekkolyth/ui/*`. The canonical name exists only so the consuming
17
+ bundler can resolve package-internal references when transpiling source.
165
18
 
166
19
  ### Next.js Setup
167
20
 
168
21
  ```tsx
169
22
  // app/layout.tsx
170
- import { ThemeProvider, ThemeScript, Toaster } from '@ekkolyth/ui/themes/provider'
171
- import '@ekkolyth/ui/themes'
23
+ import {
24
+ ThemeProvider,
25
+ ThemeScript,
26
+ Toaster,
27
+ } from "@ekkolyth/ui/themes/provider";
28
+ import "@ekkolyth/ui/themes";
172
29
 
173
30
  export default function RootLayout({ children }) {
174
31
  return (
175
32
  <html>
176
33
  <head>
177
- <ThemeScript defaultAppearance='system' />
34
+ <ThemeScript defaultAppearance="system" />
178
35
  </head>
179
36
  <body>
180
37
  <ThemeProvider
181
- defaultAppearance='system'
182
- lightTheme={['latte', 'ekkolyth-light']}
183
- darkTheme={['mocha', 'ekkolyth-dark']}
38
+ defaultAppearance="system"
39
+ lightTheme={["latte", "ekkolyth-light"]}
40
+ darkTheme={["mocha", "ekkolyth-dark"]}
184
41
  >
185
42
  {children}
186
43
  <Toaster />
187
44
  </ThemeProvider>
188
45
  </body>
189
46
  </html>
190
- )
47
+ );
191
48
  }
192
49
 
193
50
  // app/page.tsx
194
- 'use client'
195
- import { Button } from '@ekkolyth/ui'
196
- import { ThemeToggle, useTheme } from '@ekkolyth/ui/themes/provider'
51
+ ("use client");
52
+ import { Button } from "@ekkolyth/ui";
53
+ import { ThemeToggle, useTheme } from "@ekkolyth/ui/themes/provider";
197
54
 
198
55
  export default function Page() {
199
- const { appearance, setAppearance } = useTheme()
56
+ const { appearance, setAppearance } = useTheme();
200
57
 
201
58
  return (
202
59
  <div>
203
60
  <ThemeToggle />
204
- <Button onClick={() => setAppearance('dark')}>
205
- Set Dark Mode
206
- </Button>
61
+ <Button onClick={() => setAppearance("dark")}>Set Dark Mode</Button>
207
62
  </div>
208
- )
63
+ );
209
64
  }
210
65
  ```
211
66
 
212
- ## Release
67
+ ### TanStack Start Setup
213
68
 
214
- 1. On the feature branch, describe the change: `bun run changeset`.
215
- 2. Merge to `main`. `.github/workflows/release-packages.yml` consumes the
216
- Changesets plan, bumps the manifest and changelog, builds the package, and
217
- runs `scripts/stage-public.ts` to produce the `@ekkolyth/ui` artifact with its
218
- curated `dist/` exports.
219
- 3. `scripts/validate-package.ts` checks the staged tarball before anything
220
- ships.
221
- 4. The workflow publishes the validated tarball, commits the release state,
222
- and tags the release `ui-v<version>`.
223
-
224
- A push to `main` with no pending changeset is a no-op — `changeset version`
225
- has nothing to consume, and the commit/publish steps detect the unchanged
226
- manifest and stop.
69
+ ```css
70
+ /* src/styles.css */
71
+ @import "tailwindcss";
72
+ @import "@ekkolyth/ui/base.css";
73
+ @import "@ekkolyth/ui/themes";
74
+ ```
227
75
 
228
- ### Storybook + vitest
76
+ ```tsx
77
+ // src/routes/__root.tsx
78
+ import {
79
+ createRootRoute,
80
+ HeadContent,
81
+ Outlet,
82
+ Scripts,
83
+ } from "@tanstack/react-router";
84
+ import {
85
+ ThemeProvider,
86
+ ThemeScript,
87
+ Toaster,
88
+ } from "@ekkolyth/ui/themes/provider";
89
+ import appCss from "../styles.css?url";
90
+
91
+ export const Route = createRootRoute({
92
+ head: () => ({ links: [{ rel: "stylesheet", href: appCss }] }),
93
+ component: RootLayout,
94
+ });
95
+
96
+ function RootLayout() {
97
+ return (
98
+ <html suppressHydrationWarning>
99
+ <head>
100
+ <HeadContent />
101
+ <ThemeScript defaultAppearance="system" />
102
+ </head>
103
+ <body>
104
+ <ThemeProvider
105
+ defaultAppearance="system"
106
+ lightTheme={["latte", "ekkolyth-light"]}
107
+ darkTheme={["mocha", "ekkolyth-dark"]}
108
+ >
109
+ <Outlet />
110
+ <Toaster />
111
+ </ThemeProvider>
112
+ <Scripts />
113
+ </body>
114
+ </html>
115
+ );
116
+ }
117
+ ```
229
118
 
230
- The package's storybook (`.storybook/main.ts`) and vitest (`vitest.config.ts`)
231
- configs include `@ekkolyth/ui` in their alias maps so stories and unit tests resolve
232
- internal imports the same way the dev/prod bundler does.
119
+ `ThemeScript` goes in `<head>` with the same props as the `ThemeProvider` it
120
+ pairs with — it applies the stored palette before first paint, so skipping it
121
+ just costs one frame of the wrong theme.
package/dist/base.css CHANGED
@@ -179,6 +179,10 @@
179
179
  }
180
180
  }
181
181
 
182
+ .ts-chart-tooltip {
183
+ --ts-chart-tooltip-font: 500 1rem / 1.5 var(--font-sans);
184
+ }
185
+
182
186
  .prose h1,
183
187
  .prose h2,
184
188
  .prose h3 {
@@ -231,7 +235,9 @@
231
235
  .prose thead th,
232
236
  .prose tbody td,
233
237
  .prose tfoot td {
238
+ /* biome-ignore lint/complexity/noImportantStyles: pinned by base.css.test.ts to beat the typography plugin */
234
239
  padding-top: 0.375rem !important;
240
+ /* biome-ignore lint/complexity/noImportantStyles: pinned by base.css.test.ts to beat the typography plugin */
235
241
  padding-bottom: 0.375rem !important;
236
242
  border-right: 1px solid color-mix(in oklab, var(--border) 40%, transparent);
237
243
  }
package/dist/blocks.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as ColorPicker, n as Field, r as validate, t as IconPicker } from "./index-DHF8OR1R.js";
1
+ import { a as ComboboxField, i as Dropzone, n as Field, o as ComboboxFieldProps, r as validate, s as ColorPicker, t as IconPicker } from "./index-BJs5qfKG.js";
2
2
  import { n as LoadingScreenProps, t as LoadingScreen } from "./index-DsLGf2HX.js";
3
3
  import { n as WidgetLinkProps, r as WidgetProps, t as Widget } from "./index-qji4ohJZ.js";
4
- export { ColorPicker, Field, IconPicker, LoadingScreen, type LoadingScreenProps, Widget, type WidgetLinkProps, type WidgetProps, validate };
4
+ export { ColorPicker, ComboboxField, type ComboboxFieldProps, Dropzone, Field, IconPicker, LoadingScreen, type LoadingScreenProps, Widget, type WidgetLinkProps, type WidgetProps, validate };
package/dist/blocks.js CHANGED
@@ -1,4 +1,4 @@
1
- import { R as validate, d as Field, t as IconPicker, z as ColorPicker } from "./icon-picker-ByfHyJ7i.js";
2
- import { t as LoadingScreen } from "./loading-screen-DB3youyT.js";
1
+ import { _ as validate, at as ColorPicker, b as ComboboxField, t as IconPicker, u as Field, v as Dropzone } from "./icon-picker-CwavOWKf.js";
2
+ import { t as LoadingScreen } from "./loading-screen-DilmTJ3J.js";
3
3
  import { t as Widget } from "./widget-C2vjxQms.js";
4
- export { ColorPicker, Field, IconPicker, LoadingScreen, Widget, validate };
4
+ export { ColorPicker, ComboboxField, Dropzone, Field, IconPicker, LoadingScreen, Widget, validate };
package/dist/convex.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as LoadingScreen } from "./loading-screen-DB3youyT.js";
1
+ import { t as LoadingScreen } from "./loading-screen-DilmTJ3J.js";
2
2
  export { LoadingScreen };
package/dist/hooks.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { t as useIsMobile } from "./use-mobile-BVGje-2e.js";
2
- export { useIsMobile };
1
+ import { n as useIsMobile, t as useWindowScrollMargin } from "./use-window-scroll-margin-BeVjX6Le.js";
2
+ export { useIsMobile, useWindowScrollMargin };
package/dist/hooks.js CHANGED
@@ -1,2 +1,3 @@
1
- import { t as useIsMobile } from "./use-mobile-CqHXB_Nc.js";
2
- export { useIsMobile };
1
+ import { t as useIsMobile } from "./use-mobile-C-Ak14Ip.js";
2
+ import { t as useWindowScrollMargin } from "./use-window-scroll-margin-BQMpLEeT.js";
3
+ export { useIsMobile, useWindowScrollMargin };