@fulgurite/tailwind-ui
v0.0.2
Published
Controlled React switchers and date picker for Fulgurite Tailwind CSS apps.
Maintainers
Readme
@fulgurite/tailwind-ui
Controlled React switchers and date pickers for Fulgurite apps that use Tailwind CSS. The package exposes exactly six public ESM component subpath imports, plus CSS entries that register the package with Tailwind v4 using package-name imports.
Install
Install the package with the peers used by the app that renders the components.
pnpm add @fulgurite/tailwind-ui react react-dom tailwindcssAdd daisyUI only when importing from /daisy/*.
pnpm add @fulgurite/tailwind-ui react react-dom tailwindcss daisyuidaisyUI is an optional peer dependency. It is not required for /vanilla/* imports.
Compatibility
| Tool | Supported version |
| --- | --- |
| Node | >=20 |
| pnpm | 10.24.0 |
| TypeScript | ^5 |
| React | ^18.2 || ^19 |
| Tailwind CSS | ^4 |
| daisyUI | ^5, only for /daisy/* imports |
Public Imports
Import only from the exact subpaths below. There is no root import, no grouped import, no wildcard export, and no default export.
| Subpath | Named component export | Type exports |
| --- | --- | --- |
| @fulgurite/tailwind-ui/vanilla/locale-switcher | VanillaLocaleSwitcher | LocaleOption, LocaleSwitcherProps |
| @fulgurite/tailwind-ui/daisy/locale-switcher | DaisyLocaleSwitcher | LocaleOption, LocaleSwitcherProps |
| @fulgurite/tailwind-ui/vanilla/dark-mode-switcher | VanillaDarkModeSwitcher | ThemeSwitcherOption, DarkModeSwitcherProps |
| @fulgurite/tailwind-ui/daisy/dark-mode-switcher | DaisyDarkModeSwitcher | ThemeSwitcherOption, DarkModeSwitcherProps |
| @fulgurite/tailwind-ui/vanilla/date-picker | VanillaDatePicker | DatePickerProps, DatePickerWeekStart |
| @fulgurite/tailwind-ui/daisy/date-picker | DaisyDatePicker | DatePickerProps, DatePickerWeekStart |
import {
VanillaLocaleSwitcher,
type LocaleOption,
type LocaleSwitcherProps,
} from "@fulgurite/tailwind-ui/vanilla/locale-switcher";
import {
DaisyLocaleSwitcher,
type LocaleOption,
type LocaleSwitcherProps,
} from "@fulgurite/tailwind-ui/daisy/locale-switcher";
import {
VanillaDarkModeSwitcher,
type ThemeSwitcherOption,
type DarkModeSwitcherProps,
} from "@fulgurite/tailwind-ui/vanilla/dark-mode-switcher";
import {
DaisyDarkModeSwitcher,
type ThemeSwitcherOption,
type DarkModeSwitcherProps,
} from "@fulgurite/tailwind-ui/daisy/dark-mode-switcher";
import {
VanillaDatePicker,
type DatePickerProps,
type DatePickerWeekStart,
} from "@fulgurite/tailwind-ui/vanilla/date-picker";
import {
DaisyDatePicker,
type DatePickerProps,
type DatePickerWeekStart,
} from "@fulgurite/tailwind-ui/daisy/date-picker";Tailwind v4 Setup
Import the package CSS entry in the consuming app CSS so Tailwind scans classes shipped in dist. The package CSS contains the required Tailwind @source directives, so app stylesheets do not need relative node_modules paths.
@import "tailwindcss";
@import "@fulgurite/tailwind-ui/tailwind.css";Use narrower CSS entries when the app imports only one variant family.
@import "tailwindcss";
@import "@fulgurite/tailwind-ui/vanilla.css";@import "tailwindcss";
@import "@fulgurite/tailwind-ui/daisy.css";daisyUI Setup
Only apps that use /daisy/* imports need daisyUI. The /vanilla/date-picker entry uses Tailwind utilities only and does not require daisyUI. Add the plugin in the same consumer CSS file that imports Tailwind when rendering daisyUI components.
@import "tailwindcss";
@import "@fulgurite/tailwind-ui/daisy.css";
@plugin "daisyui";Consumer apps own daisyUI theme selection.
@import "tailwindcss";
@import "@fulgurite/tailwind-ui/daisy.css";
@plugin "daisyui" {
themes: light --default, dark --prefersdark, cupcake;
}Usage Examples
All components are controlled React components. Switchers receive the current value, a stable options array, and an onChange handler. Both date pickers share the same API: a controlled date-only value string and an onChange handler that updates application state.
import { useState } from "react";
import {
VanillaLocaleSwitcher,
type LocaleOption,
} from "@fulgurite/tailwind-ui/vanilla/locale-switcher";
const localeOptions: readonly LocaleOption[] = [
{ value: "en", label: "English", flag: "US" },
{ value: "es", label: "Spanish", flag: "ES" },
];
export function VanillaLocaleControl() {
const [locale, setLocale] = useState("en");
return (
<VanillaLocaleSwitcher
value={locale}
options={localeOptions}
onChange={(nextLocale) => setLocale(nextLocale)}
label="Language"
/>
);
}import { useState } from "react";
import {
DaisyLocaleSwitcher,
type LocaleOption,
} from "@fulgurite/tailwind-ui/daisy/locale-switcher";
const localeOptions: readonly LocaleOption[] = [
{ value: "en", label: "English", flag: "US" },
{ value: "fr", label: "French", flag: "FR" },
];
export function DaisyLocaleControl() {
const [locale, setLocale] = useState("en");
return (
<DaisyLocaleSwitcher
value={locale}
options={localeOptions}
onChange={(nextLocale) => setLocale(nextLocale)}
label="Language"
/>
);
}import { useState } from "react";
import {
VanillaDarkModeSwitcher,
type ThemeSwitcherOption,
} from "@fulgurite/tailwind-ui/vanilla/dark-mode-switcher";
const themeOptions: readonly ThemeSwitcherOption[] = [
{ value: "light", label: "Light" },
{ value: "dark", label: "Dark" },
];
export function VanillaThemeControl() {
const [theme, setTheme] = useState("light");
return (
<VanillaDarkModeSwitcher
value={theme}
options={themeOptions}
onChange={(nextTheme) => setTheme(nextTheme)}
label="Theme"
/>
);
}import { useState } from "react";
import {
DaisyDarkModeSwitcher,
type ThemeSwitcherOption,
} from "@fulgurite/tailwind-ui/daisy/dark-mode-switcher";
const themeOptions: readonly ThemeSwitcherOption[] = [
{ value: "light", label: "Light" },
{ value: "dark", label: "Dark" },
];
export function DaisyThemeControl() {
const [theme, setTheme] = useState("light");
return (
<DaisyDarkModeSwitcher
value={theme}
options={themeOptions}
onChange={(nextTheme) => setTheme(nextTheme)}
label="Theme"
/>
);
}import { useState } from "react";
import { VanillaDatePicker } from "@fulgurite/tailwind-ui/vanilla/date-picker";
export function VanillaDeliveryDateControl() {
const [deliveryDate, setDeliveryDate] = useState("");
return (
<VanillaDatePicker
value={deliveryDate}
onChange={(nextDate) => setDeliveryDate(nextDate)}
label="Delivery date"
emptyLabel="No date selected"
openCalendarLabel="Open delivery calendar"
allowClear
/>
);
}import { useState } from "react";
import { DaisyDatePicker } from "@fulgurite/tailwind-ui/daisy/date-picker";
export function DaisyDeliveryDateControl() {
const [deliveryDate, setDeliveryDate] = useState("");
return (
<DaisyDatePicker
value={deliveryDate}
onChange={(nextDate) => setDeliveryDate(nextDate)}
label="Delivery date"
emptyLabel="No date selected"
openCalendarLabel="Open delivery calendar"
allowClear
/>
);
}API Reference
VanillaLocaleSwitcher and DaisyLocaleSwitcher
| Prop | Type | Required | Notes |
| --- | --- | --- | --- |
| value | string | Yes | Current selected locale value. |
| options | readonly LocaleOption[] | Yes | Ordered choices rendered by the switcher. |
| onChange | (value: string, option: LocaleOption) => void | Yes | Called when an enabled option is selected. |
| label | React.ReactNode | No | Accessible label; vanilla defaults to Language, daisy falls back to Change language for accessibility. |
| emptyLabel | React.ReactNode | No | Empty-state text when no locale options are available. |
| disabled | boolean | No | Disables the trigger and option changes. |
| className | string | No | Class name for the root element. |
| triggerClassName | string | No | Class name merged onto the trigger button. |
| menuClassName | string | No | Class name merged onto the option menu. |
| optionClassName | string | No | Class name merged onto each option button. |
| triggerIcon | React.ReactNode | No | Replaces the internal globe icon. |
| LocaleOption field | Type | Required | Notes |
| --- | --- | --- | --- |
| value | string | Yes | Stable value passed back to onChange. |
| label | React.ReactNode | Yes | Visible option label. |
| flag | React.ReactNode | No | Optional flag or locale marker. |
| disabled | boolean | No | Disables only this option. |
VanillaDarkModeSwitcher and DaisyDarkModeSwitcher
| Prop | Type | Required | Notes |
| --- | --- | --- | --- |
| value | string | Yes | Current selected theme value. |
| options | readonly ThemeSwitcherOption[] | Yes | Ordered choices rendered by the switcher. |
| onChange | (value: string, option: ThemeSwitcherOption) => void | Yes | Called when an enabled option is selected. |
| label | React.ReactNode | No | Accessible legend rendered for assistive technology. |
| disabled | boolean | No | Disables the switcher and option changes. |
| className | string | No | Class name for the fieldset/root element. |
| optionClassName | string | No | Class name merged onto each option button. |
| ThemeSwitcherOption field | Type | Required | Notes |
| --- | --- | --- | --- |
| value | string | Yes | Stable value passed back to onChange. |
| label | React.ReactNode | Yes | Visible label when no icon is provided; accessible label when an icon is provided. |
| icon | React.ReactNode | No | Optional icon for compact theme choices. |
| disabled | boolean | No | Disables only this option. |
VanillaDatePicker and DaisyDatePicker
| Prop | Type | Required | Notes |
| --- | --- | --- | --- |
| value | string | Yes | Controlled date value. Use "" for no date and YYYY-MM-DD for selected dates. Invalid non-empty values display as raw text and do not select a calendar day. |
| onChange | (nextValue: string) => void | Yes | Called with a YYYY-MM-DD date string when a day is selected, or "" when the clear action runs. |
| label | React.ReactNode | No | Visible field label and dialog label when it is a string or number. |
| emptyLabel | React.ReactNode | No | Trigger text when value is empty. Defaults to Select date. |
| openCalendarLabel | string | No | Accessible trigger label. Defaults to Open calendar. |
| clearLabel | string | No | Accessible clear-button label. Defaults to Clear date. |
| previousMonthLabel | string | No | Accessible previous-month button label. |
| nextMonthLabel | string | No | Accessible next-month button label. |
| dateInputLabel | string | No | Accessible label for the hidden native date input affordance. |
| disabled | boolean | No | Disables trigger, clear, month navigation, and day selection. |
| allowClear | boolean | No | Shows a clear button when value is non-empty. |
| locale | string | No | Locale passed to date formatting. Defaults to en-US. |
| weekStartsOn | DatePickerWeekStart | No | First day of the week, 0 for Sunday through 6 for Saturday. Defaults to Sunday. |
| className | string | No | Class name for the root element. |
| triggerClassName | string | No | Class name merged onto the trigger button. |
| panelClassName | string | No | Class name merged onto the calendar popover panel. |
| calendarClassName | string | No | Class name merged onto the calendar content grid. |
| dayClassName | string | No | Class name merged onto every day button. |
| clearButtonClassName | string | No | Class name merged onto the clear button. |
DatePickerWeekStart is the union 0 | 1 | 2 | 3 | 4 | 5 | 6.
Runtime Boundaries
The components do not inject CSS, register Tailwind plugins, mutate document, read or write storage, route between locales, persist themes, or own app state. React, React DOM, Tailwind CSS, and optional daisyUI setup stay in the consuming app.
Locale switchers use triggerIcon when supplied; otherwise they render the package globe icon. Theme options with icon render the icon with an accessible label from label; options without icon render label visibly. Date pickers own only transient UI state for their open panel and viewed month while all selected date state stays in the caller.
SSR and Next Apps
Each public entry starts with the React client boundary and renders from props. The components do not read browser APIs at module scope, and they do not read from local storage, cookies, or the router. The date picker attaches document/window listeners only in effects while the popover is open. In Next apps, keep state, persistence, locale routing, and theme application in the app layer; handlers that touch browser APIs belong in a client component.
Tiery Migration Notes
When migrating from Tiery, preserve the user-facing behavior from LanguageSwitcher and ThemePicker: present the current language or theme, show available choices, and call app code when the user selects a new value.
Do not port Tiery runtime coupling into this package. Next routing, i18n adapters, Zustand stores, local storage, and document theme updates belong in the consuming app.
Package Readiness
The package is ESM-only and exposes no alternate module-format fields. Packed contents are controlled by the files field and verified to include only package.json, README.md, LICENSE, and allowed .js, .js.map, .d.ts, .d.ts.map, or .css files under dist.
Release-readiness checks are available through pnpm verify:all, which runs Biome checks, type checking, build, unit tests, Storybook build, Playwright Storybook QA, export verification, pack validation, and fixture verification in order. This README documents package readiness only; it does not claim that an npm release has been made.
