Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
2889a56
[migration] Phase 0: visual regression baseline + showcase preview
librowski May 5, 2026
e0d1651
[migration] Phase 1: BaseButton, Switch, Input -> @base-ui/react
librowski May 5, 2026
be19185
[migration] Phase 2: TextArea, Modal -> base-ui + drop @mui/material/…
librowski May 5, 2026
54bfbd0
[migration] Phase 3: Menu, Select -> @base-ui/react
librowski May 5, 2026
dae58ca
[migration] Phase 4+5: Snackbar wrapper drop, @mui/base removed
librowski May 5, 2026
54738e3
[research] Bundle slimming options + bundle-stats tooling
librowski May 5, 2026
ff6428f
[migration] Phase 6: Tooltip -> @base-ui/react/tooltip + drop @floati…
librowski May 5, 2026
8fd2899
[migration] Phase 7: DatePicker rebuild on react-day-picker, drop @ma…
librowski May 5, 2026
3abc75d
[migration] Phase 8: multi-entry build with subpath exports
librowski May 5, 2026
cc4eab4
[migration] Phase 9: enter/exit fade animations for Modal/Menu/Select…
librowski May 5, 2026
2ede11c
[migration] Phase 10: externalize @base-ui/react + @phosphor-icons/re…
librowski May 5, 2026
0ad3077
[migration] backwards-compat shims for v1 consumers
librowski May 5, 2026
a7c5035
[migration] DatePicker pixel-perfect parity with Mantine
librowski May 5, 2026
a77dd52
[migration] tighten @base-ui/react peer range to ^1.4.0
librowski Jun 10, 2026
da35493
[migration] Menu: honest onOpenChange signature (breaking)
librowski Jun 10, 2026
c763db8
[migration] DatePicker: own design-system styles, drop Mantine emulation
librowski Jun 10, 2026
f0834a3
[migration] Switch: restore keyboard accessibility (focusable root)
librowski Jun 10, 2026
4357c22
[migration] add Playwright interaction tests for behavior and a11y
librowski Jun 10, 2026
98bd5dd
[migration] fix CSS layer order in every emitted stylesheet
librowski Jun 10, 2026
fb9c52e
[migration] Menu: correct physical-to-logical offset mapping
librowski Jun 10, 2026
f970e58
[migration] DatePicker: fix selection semantics in all three modes
librowski Jun 11, 2026
cb1873e
[migration] list items: restore selected/disabled styling via data at…
librowski Jun 11, 2026
a849bd8
[migration] Input: restore the error prop and root state styling
librowski Jun 11, 2026
e4b35b8
[migration] Menu/Select: put open/close transitions on the Popup element
librowski Jun 11, 2026
f4d622e
[migration] Tooltip: controlled-mode hover parity + composing trigger…
librowski Jun 11, 2026
a46818c
[migration] regenerate website prop docs (Input error prop)
librowski Jun 11, 2026
8ec9a99
fix(build): externalize react-textarea-autosize in Vite config
librowski Jun 16, 2026
5d24674
remove internal research docs from the repo
librowski Jun 16, 2026
e53b41e
remove migration planning doc from the repo
librowski Jun 16, 2026
b8926a7
move @phosphor-icons/react from peer to bundled dependency
librowski Jun 17, 2026
99686c2
fix(ui): consistent CSS layers, handle positioning, and PR cleanup
librowski Jun 17, 2026
c3217cb
refactor(ui): replace explicit subpath exports with wildcard pattern
librowski Jun 17, 2026
d751ed9
chore: update lockfile after removing @playwright/test
librowski Jun 17, 2026
edc0fed
fix(ui): clean up preview page and fix missing CSS in date-picker/too…
librowski Jun 17, 2026
5e87162
fix(ui): default Button type to "button" to prevent implicit form submit
librowski Jun 19, 2026
10ce6e4
fix(ui): parse date-only DatePicker strings as local time
librowski Jun 19, 2026
3c02363
feat(ui): expose styles.css entry for subpath consumers
librowski Jun 19, 2026
0f0f0d5
docs(ui): add 2.0 CHANGELOG and correct README to Base UI
librowski Jun 19, 2026
4023189
refactor(ui): derive Menu side/align from Base UI types
librowski Jun 22, 2026
7dd4b70
fix(ui): type Switch onChange event as the native Event
librowski Jun 22, 2026
427a8a1
refactor(ui): drive Tooltip arrow and variants from CSS
librowski Jun 22, 2026
ed9e7e0
refactor(ui): drop migration-narration comment from Input
librowski Jun 22, 2026
c1aabc8
refactor(ui): extract combine-css-bundle plugin from vite config
librowski Jun 22, 2026
ca31e3f
docs(ui): recommend barrel import, note subpath as an option
librowski Jun 22, 2026
d853a39
refactor(ui): move css-layers doc to package root
librowski Jun 22, 2026
479ddc4
refactor(ui): update css-layers doc reference after move
librowski Jun 22, 2026
96f3881
refactor(ui): establish CSS layer order via styles.css contract
librowski Jun 22, 2026
c38d14d
chore(ui): set version to 1.0.0-beta.28
librowski Jun 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ packages/website/docs/generated-docs/*
packages/website/.docusaurus
packages/website/.cache-loader
packages/website/static/css/*

# Playwright
packages/ui/playwright-report
packages/ui/test-results
.playwright-mcp
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Add files here to ignore them from prettier formatting
/dist
/coverage
.claude/worktrees
.claude/launch.json

packages/tokens/tokens.json
packages/tokens/tokens.json
Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ Developed and maintained by **[Synergy Codes](https://www.synergycodes.com/)**.

### 📦 Installation

Use one of the commands below to add **Overflow UI** to your project:
Overflow UI relies on `@base-ui/react` (headless component primitives) and `@phosphor-icons/react` (icon set) as peer dependencies. Install all three at once:

```bash
npm install @synergycodes/overflow-ui
npm install @synergycodes/overflow-ui @base-ui/react @phosphor-icons/react
```

```bash
pnpm add @synergycodes/overflow-ui
pnpm add @synergycodes/overflow-ui @base-ui/react @phosphor-icons/react
```

```bash
yarn add @synergycodes/overflow-ui
yarn add @synergycodes/overflow-ui @base-ui/react @phosphor-icons/react
```

### 🎨 Import styles
Expand All @@ -44,14 +44,18 @@ To make the styles use proper variables, include `data-theme` (`light` or `dark`

### 🎛️ Use components

The library has a per-component subpath export, so you can import only what you use and let your bundler tree-shake the rest:

```tsx
import { Input } from '@synergycodes/overflow-ui';
import { Input } from '@synergycodes/overflow-ui/input';

// …

<Input value={value} onChange={onChange} />;
```

The barrel import (`from '@synergycodes/overflow-ui'`) is also supported and tree-shakes equally well in modern bundlers.

### Customization

Each Overflow UI component uses CSS variables that are derived from primitive values.
Expand Down
61 changes: 61 additions & 0 deletions packages/ui/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Changelog

All notable changes to `@synergycodes/overflow-ui` are documented in this file.

## 1.0.0-beta.28

This beta rebuilds the library on [Base UI](https://base-ui.com/) and removes
the previous MUI / Mantine / Emotion / Floating UI stack. It contains breaking
changes relative to `1.0.0-beta.27`; read the sections below before upgrading.

### Breaking changes

#### Dependencies

- Removed `@mui/material`, `@mui/base`, `@mantine/core`, `@mantine/dates`,
`@emotion/*`, and `@floating-ui/react`.
- `@base-ui/react` (`^1.4.0`) is now a **peer dependency** - consumers must
install it alongside this package.
- `DatePicker` is rebuilt on `react-day-picker` + `date-fns`; `TextArea` on
`react-textarea-autosize`. `date-fns`, `react-day-picker`, and `clsx` are
bundled into the package output.

#### Packaging

- The build moved from a single bundle to a **multi-entry build** with a
per-component subpath export: `@synergycodes/overflow-ui/<component>` (e.g.
`@synergycodes/overflow-ui/date-picker`).
- Importing from the package root still injects all required styles. Importing
per-component entries injects only that component's CSS, so add
`@synergycodes/overflow-ui/styles.css` once for the global layer order,
reset, and typography.
- The old `overflow-ui.js` bundle filename is retained as a compatibility shim
that re-exports the new `index.js` entry, so hard-coded paths keep resolving.

#### Component API

- **DatePicker**: the prop surface no longer forwards the full Mantine prop set.
It now accepts a curated list: `value`, `defaultValue`, `type`,
`valueFormat`, `placeholder`, `error`, `size`, `disabled`, `minDate`,
`maxDate`, `onChange`, `id`, `className`, `aria-label`, `aria-labelledby`.
- `valueFormat` uses `date-fns` tokens (e.g. `dd/MM/yyyy`). The legacy
`DD/MM/YYYY` (dayjs) default is accepted and converted for compatibility.
- In `range` mode, `onChange` fires `null` while a range is mid-selection and
emits the completed `[from, to]` tuple once both ends are picked.
- **Menu**: `onOpenChange` signature is now `(open: boolean, event?: Event)`.
The MUI `slotProps` / passthrough surface is no longer available.
- **Select**: `onChange` signature is `(event, value)` - the event is the first
argument, the selected value the second.
- **Switch**: `onChange` is `(checked: boolean, event: Event)`. The second
argument is the native DOM event (previously typed as a React
`ChangeEvent<HTMLInputElement>`, which never matched the value passed at
runtime).
- Transitions moved to the popup element, where Base UI sets
`data-starting-style` / `data-ending-style`.

### Added

- `@synergycodes/overflow-ui/styles.css` - standalone global stylesheet (layer
order, reset, typography) for per-component / subpath consumers.
- `Input` gains a typed `error` prop wired to the error state.
- `Snackbar` exposes proper `role="status"` / `aria-live` semantics.
29 changes: 14 additions & 15 deletions packages/ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ yarn add @synergycodes/overflow-ui

### 🎨 Import styles

Add to your style sheet or component:
Import components from the package root (the recommended default). Their styles,
including the global layer order, reset, and typography, are injected
automatically, so you only need to add the design tokens:

```css
@import '@synergycodes/overflow-ui/tokens.css';
Expand All @@ -34,12 +36,18 @@ Add to your style sheet or component:
import '@synergycodes/overflow-ui/tokens.css';
```

> **Subpath imports.** You can also import a single component directly, e.g.
> `import { DatePicker } from '@synergycodes/overflow-ui/date-picker'`. That
> injects only the component's own CSS, so import the global stylesheet once and
> **before any component**, so the cascade layers are ordered correctly:
> `import '@synergycodes/overflow-ui/styles.css'`.

### 🎛️ Apply the Theme

To make the styles use proper variables, include data-theme (light or dark) attribute in <html>:

```html
<html data-theme="light">
<html data-theme="light"></html>
```

### 🧱 Use components
Expand Down Expand Up @@ -88,7 +96,6 @@ or a derived value used by the selected component:

Overflow UI uses [CSS layers](https://developer.mozilla.org/en-US/docs/Web/CSS/@layer) to separate its styles from yours. By default, CSS styles outside of any layer take precedence over what Overflow UI defines, so your styles will always win the specificity war. You can customize Overflow UI components with simple `input {}`.


```css
@layer ui.component {
.separator {
Expand All @@ -98,6 +105,7 @@ Overflow UI uses [CSS layers](https://developer.mozilla.org/en-US/docs/Web/CSS/@
```

Default Overflow UI order:

```css
@layer ui.base, ui.component;
```
Expand Down Expand Up @@ -129,20 +137,11 @@ Edit `ui/preview-page/preview-page.tsx` to display desired components.

### 📣 Important Note on Underlying Technology

> **Overflow UI is built on top of [MUI Base](https://v6.mui.com/base-ui/getting-started/), a headless component library that focuses on accessibility and logic, while leaving the styling up to us.**
>
> Thanks to MUI Base, Overflow UI provides components that are **accessible by default** and **fully customizable** through our design tokens.
>
> We are aware that **MUI Base has been deprecated**, and the MUI team recommends migrating to [Base UI](https://base-ui.com).
> However, after careful evaluation, we've chosen to **stay with MUI Base** for now because:
>
> * ✅ **Base UI is not yet mature enough** for our needs.
> * ✅ We want to ensure a stable, well-tested experience for Overflow UI users.
> **Overflow UI is built on top of [Base UI](https://base-ui.com), a headless component library that focuses on accessibility and logic, while leaving the styling up to us.**
>
> This is a **conscious and informed decision**.
> We will continue to monitor Base UI's progress and will consider migrating when we feel it's the right time, ensuring a smooth and thoughtful transition for Overflow UI users.
> Thanks to Base UI, Overflow UI provides components that are **accessible by default** and **fully customizable** through our design tokens.
>
> If you have any questions or concerns, feel free to reach out — we’re happy to share our reasoning and plans in more detail!
> Earlier `1.0.0` betas were built on the now-deprecated [MUI Base](https://v6.mui.com/base-ui/getting-started/). From `1.0.0-beta.28` the library is built on Base UI; see [CHANGELOG.md](./CHANGELOG.md) for the full list of changes.

## Showcase

Expand Down
67 changes: 67 additions & 0 deletions packages/ui/combine-css-bundle.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import fs from 'node:fs';
import { resolve } from 'node:path';
import type { Plugin } from 'vite';

/**
* Post-build CSS steps for the multi-entry library bundle. See css-layers.md.
*
* - emits `index.css` (all component styles, prefixed with the @layer order)
* and `styles.css` (the global layer order, reset and typography);
* - emits an `overflow-ui.js` shim re-exporting the renamed `index.js` entry.
*
* Per-component stylesheets do not carry the @layer declaration; consumers
* establish the order by importing `styles.css` first (or the barrel).
*/
export function combineCssBundle(rootDir: string): Plugin {
const distDir = resolve(rootDir, 'dist');
const stylesDir = resolve(rootDir, 'src/styles');

return {
name: 'overflow-ui:combine-css-bundle',
apply: 'build',
closeBundle() {
writeCombinedStylesheet(distDir, stylesDir);
writeGlobalStylesheet(distDir, stylesDir);
writeLegacyEntryShim(distDir);
},
};
}

function readLayerOrder(stylesDir: string): string {
return fs.readFileSync(resolve(stylesDir, 'layers.css'), 'utf-8').trim();
}

function writeCombinedStylesheet(distDir: string, stylesDir: string) {
const assetsDir = resolve(distDir, 'assets');
if (!fs.existsSync(assetsDir)) return;

const styles = fs
.readdirSync(assetsDir)
.filter((file) => file.endsWith('.css'))
.sort()
.map((file) => fs.readFileSync(resolve(assetsDir, file), 'utf-8'))
.join('\n');

// index.css is consumed standalone, so it declares the @layer order itself.
const combined = `${readLayerOrder(stylesDir)}\n${styles}`;
fs.writeFileSync(resolve(distDir, 'index.css'), combined);
}

function writeGlobalStylesheet(distDir: string, stylesDir: string) {
const globals = ['layers.css', 'globals.css', 'typography.css']
.map((file) => fs.readFileSync(resolve(stylesDir, file), 'utf-8'))
.join('\n');

fs.writeFileSync(resolve(distDir, 'styles.css'), globals);
}

function writeLegacyEntryShim(distDir: string) {
fs.writeFileSync(
resolve(distDir, 'overflow-ui.js'),
`export * from './index.js';\n`,
);
fs.writeFileSync(
resolve(distDir, 'overflow-ui.d.ts'),
`export * from './index';\n`,
);
}
32 changes: 32 additions & 0 deletions packages/ui/css-layers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# CSS layers

Overflow UI emits all of its styles into two ordered cascade layers:

```css
@layer ui.base, ui.component;
```

`ui.base` holds resets and primitives; `ui.component` holds component styles.
Declaring the order once, before any rule from either layer, guarantees that
`ui.component` always wins over `ui.base`, and that unlayered consumer styles
win over both.

## Establishing the order

The order is fixed by the **first** `@layer` declaration the browser sees, so
the declaration must load before any component rule. The declaration lives in
`styles.css` (and in the package barrel, which imports it first), so consumers
establish it by either:

- importing from the package root - the barrel imports the declaration first; or
- importing `@synergycodes/overflow-ui/styles.css` **before** any component when
using per-component subpath imports.

Per-component stylesheets deliberately do **not** repeat the declaration. If one
loads before `styles.css`, the first use of a layer fixes the order: a
`ui.component` rule seen before any `ui.base` rule locks it as
`[ui.component, ui.base]`, inverting the cascade. Importing `styles.css` first
avoids this.

The combined `index.css` (consumed standalone, e.g. by Workflow Builder) carries
the declaration at its top for the same reason.
33 changes: 19 additions & 14 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@synergycodes/overflow-ui",
"type": "module",
"version": "1.0.0-beta.27",
"version": "1.0.0-beta.28",
"description": "A React library for creating node-based UIs and diagram-driven applications. Perfect for React Flow users, providing ready-to-use node templates and components that work seamlessly with React Flow's ecosystem.",
"keywords": [
"react",
Expand Down Expand Up @@ -32,35 +32,38 @@
"dev": "vite build --watch",
"check:built-css": "tsx ./scripts/check-built-css.ts",
"preview": "vite --config preview-page/vite.preview.config.ts",
"preview:build": "vite build --config preview-page/vite.preview.config.ts",
"lint": "eslint",
"lint:fix": "eslint --fix",
"typecheck": "tsc --noEmit --pretty"
"typecheck": "tsc --noEmit --pretty",
"build:stats": "BUNDLE_STATS=1 vite build"
},
"module": "./dist/overflow-ui.js",
"module": "./dist/index.js",
"files": [
"dist"
],
"main": "./dist/overflow-ui.js",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/overflow-ui.js"
"import": "./dist/index.js"
},
"./*": {
"types": "./dist/components/*/index.d.ts",
"import": "./dist/*.js"
},
"./styles.css": "./dist/styles.css",
"./tokens.css": "./dist/tokens.css"
},
"dependencies": {
"@emotion/styled": "^11.14.0",
"@floating-ui/react": "^0.26.28",
"@mantine/core": "^7.17.2",
"@mantine/dates": "^7.17.2",
"@mui/base": "5.0.0-beta.62",
"@mui/material": "^6.4.7",
"@phosphor-icons/react": "^2.1.7",
"clsx": "^2.0.0"
"@phosphor-icons/react": "^2.0.0",
"clsx": "^2.0.0",
"date-fns": "^4.1.0",
"react-day-picker": "^9.14.0",
"react-textarea-autosize": "^8.5.6"
},
"devDependencies": {
"@emotion/react": "^11.14.0",
"@synergycodes/overflow-ui-tokens": "workspace:*",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
Expand All @@ -70,13 +73,15 @@
"postcss": "^8.5.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"rollup-plugin-visualizer": "^7.0.1",
"typescript": "^5.6.3",
"vite": "^6.2.2",
"vite-plugin-dts": "^4.5.3",
"vite-plugin-lib-inject-css": "^2.2.1",
"vite-plugin-static-copy": "^2.3.1"
},
"peerDependencies": {
"@base-ui/react": "^1.4.0",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
},
Expand Down
17 changes: 1 addition & 16 deletions packages/ui/preview-page/main.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
import { createRoot } from 'react-dom/client';
import { PreviewPage } from './preview-page';
import styles from './preview-page.module.css';

const root = createRoot(document.querySelector('#root') as HTMLElement);

root.render(
<PreviewWrapper>
<PreviewPage />
</PreviewWrapper>,
);

function PreviewWrapper({ children }: { children: React.ReactNode }) {
return (
<div className={styles['preview-container']}>
<div className={styles['preview-header']}>Components Testing</div>
<div className={styles['preview-content']}>{children}</div>
</div>
);
}
root.render(<PreviewPage />);
Loading
Loading