diff --git a/README.md b/README.md
index 0150318..9bced55 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,51 @@
-# Blazor UI Components
\ No newline at end of file
+# Enciphered.Blazor.UIComponents
+
+A **pure static SSR** Blazor component library styled with **Tailwind CSS v4** and **shadcn/ui** design tokens. All interactivity is powered by vanilla JavaScript — no SignalR, no WebAssembly, no `InteractiveServer` render mode required. Form validation and submission are handled entirely through **htmx**.
+
+## Features
+
+- **Zero Blazor interactivity** — components work with `AddRazorComponents()` alone
+- **Tailwind CSS v4** with oklch color tokens (light + dark mode)
+- **htmx form validation** — per-field blur validation and full form submission with a fluent `FormValidator` API
+- **Strongly-typed model binding** — bind submitted form data to POCOs automatically
+- **Sidebar layout** — collapsible, responsive, cookie-persisted
+- **Card components** — composable header/content/footer/image/action slots
+- **Dark mode** — toggle with localStorage persistence, FOUC-free
+- **Date/Time pickers** — calendar and time picker popovers with hidden native inputs
+
+## Quick Start
+
+```bash
+dotnet add package Enciphered.Blazor.UIComponents
+```
+
+> See the full [Getting Started guide](docs/getting-started.md) for setup instructions.
+
+## Documentation
+
+| Document | Description |
+|---|---|
+| [Getting Started](docs/getting-started.md) | Installation, prerequisites, and first-app setup |
+| [Sidebar](docs/components/sidebar.md) | Collapsible sidebar layout system |
+| [Card](docs/components/card.md) | Composable card components |
+| [Button](docs/components/button.md) | Button with variants and sizes |
+| [Form Inputs](docs/components/form-inputs.md) | TextInput, NumberInput, DateInput, TimeInput, DateTimeInput |
+| [Form Validation](docs/forms/validation.md) | htmx-powered validation with FormValidator |
+| [Form Submission](docs/forms/submission.md) | Handling form submit with model binding |
+| [Theme Toggle](docs/components/theme-toggle.md) | Dark/light mode toggle |
+
+## Architecture
+
+All interactive behavior (popovers, sidebar collapse, calendar navigation, number input steppers, form resets) is implemented via three vanilla JS modules that use `data-*` attribute selectors and event delegation:
+
+| Module | Purpose |
+|---|---|
+| `darkmode.js` | Theme toggle, localStorage persistence, SVG icon sync |
+| `sidebar.js` | Expand/collapse, mobile responsiveness, cookie persistence |
+| `forms.js` | Popover, calendar, time picker, number stepper, form reset |
+
+These modules survive Blazor enhanced navigation via `MutationObserver` patterns.
+
+## License
+
+ISC
\ No newline at end of file
diff --git a/docs/components/button.md b/docs/components/button.md
new file mode 100644
index 0000000..0bf2474
--- /dev/null
+++ b/docs/components/button.md
@@ -0,0 +1,148 @@
+# Button
+
+A styled button component with variant and size presets built from Tailwind utility classes.
+
+---
+
+## Basic Usage
+
+```razor
+
+
+
+```
+
+---
+
+## Variants
+
+Use the `Variant` parameter with constants from `ButtonVariant`:
+
+```razor
+
+
+
+
+
+
+```
+
+| Constant | Tailwind Classes |
+|---|---|
+| `ButtonVariant.Default` | `bg-primary text-primary-foreground shadow hover:bg-primary/90` |
+| `ButtonVariant.Destructive` | `bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90` |
+| `ButtonVariant.Outline` | `border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground` |
+| `ButtonVariant.Secondary` | `bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80` |
+| `ButtonVariant.Ghost` | `hover:bg-accent hover:text-accent-foreground` |
+| `ButtonVariant.Link` | `text-primary underline-offset-4 hover:underline` |
+
+You can also pass any custom Tailwind class string directly:
+
+```razor
+
+```
+
+### Creating Custom Variants
+
+Since `Variant` and `Size` are plain strings (not enums), you can create your own variant constants without modifying the library. Define a static class in your app with any Tailwind utility combinations you need:
+
+```csharp
+public static class AppButtonVariant
+{
+ public const string Success =
+ "bg-green-600 text-white shadow-sm hover:bg-green-700";
+
+ public const string Warning =
+ "bg-amber-500 text-white shadow-sm hover:bg-amber-600";
+
+ public const string Info =
+ "bg-sky-500 text-white shadow-sm hover:bg-sky-600";
+
+ public const string OutlineDestructive =
+ "border border-destructive text-destructive bg-transparent shadow-sm hover:bg-destructive/10";
+
+ public const string Gradient =
+ "bg-gradient-to-r from-purple-500 to-pink-500 text-white shadow-sm hover:from-purple-600 hover:to-pink-600";
+}
+```
+
+Then use them exactly like the built-in variants:
+
+```razor
+
+
+
+```
+
+You can do the same for custom sizes:
+
+```csharp
+public static class AppButtonSize
+{
+ public const string Xs = "h-7 rounded-md px-2 text-xs";
+ public const string Xl = "h-12 rounded-lg px-10 text-base";
+ public const string Wide = "h-9 px-12 py-2";
+}
+```
+
+```razor
+
+```
+
+This approach works because the `Button` component simply concatenates the variant and size strings into the element's `class` attribute — there is no closed set of allowed values.
+
+---
+
+## Sizes
+
+Use the `Size` parameter with constants from `ButtonSize`:
+
+```razor
+
+
+
+
+```
+
+| Constant | Tailwind Classes |
+|---|---|
+| `ButtonSize.Default` | `h-9 px-4 py-2` |
+| `ButtonSize.Sm` | `h-8 rounded-md px-3 text-xs` |
+| `ButtonSize.Lg` | `h-10 rounded-md px-8` |
+| `ButtonSize.Icon` | `h-9 w-9` |
+
+---
+
+## Button with Icon
+
+Use the `Icon` render fragment to prepend an icon:
+
+```razor
+
+```
+
+---
+
+## Parameters
+
+| Parameter | Type | Default | Description |
+|---|---|---|---|
+| `ChildContent` | `RenderFragment?` | — | Button label content |
+| `Icon` | `RenderFragment?` | — | Icon slot rendered before the label |
+| `Type` | `string` | `"button"` | HTML button type (`button`, `submit`, `reset`) |
+| `Disabled` | `bool` | `false` | Disables the button |
+| `Variant` | `string` | `ButtonVariant.Default` | Visual style classes |
+| `Size` | `string` | `ButtonSize.Default` | Size classes |
+| `Class` | `string?` | — | Additional CSS classes appended |
+
+All unmatched HTML attributes (`data-testid`, `aria-*`, etc.) are passed through via `@attributes`.
diff --git a/docs/components/card.md b/docs/components/card.md
new file mode 100644
index 0000000..0787c77
--- /dev/null
+++ b/docs/components/card.md
@@ -0,0 +1,103 @@
+# Card
+
+A composable card component suite for displaying grouped content in a bordered container with optional header, footer, image, and action slots.
+
+---
+
+## Components
+
+| Component | Description |
+|---|---|
+| `Card` | Root container with border, shadow, and rounded corners |
+| `CardHeader` | Top section — contains title, description, and optional action |
+| `CardTitle` | `
` heading styled for cards |
+| `CardDescription` | Muted paragraph below the title |
+| `CardAction` | Trailing action element (button/link) aligned to the header's right edge |
+| `CardContent` | Main body area |
+| `CardFooter` | Bottom area for buttons or metadata |
+| `CardImage` | Full-width image with optional wrapper styling |
+
+---
+
+## Basic Card
+
+```razor
+
+
+ Notifications
+ You have 3 unread messages.
+
+
+
Here is the main content of the card.
+
+
+
+
+
+```
+
+---
+
+## Card with Action
+
+The `CardAction` renders at the trailing edge of the header using CSS grid:
+
+```razor
+
+
+ Team Members
+ Manage your team.
+
+
+
+
+
+
+
+
+```
+
+---
+
+## Card with Image
+
+```razor
+
+
+
+ Beautiful Scenery
+ A mountain landscape at sunset.
+
+
+
The image fills the card width and crops via object-cover.
` |
+
+### CardTitle, CardDescription, CardContent, CardFooter, CardAction
+
+All accept `ChildContent` and `Class` parameters. All support unmatched HTML attributes via `@attributes`.
diff --git a/docs/components/form-inputs.md b/docs/components/form-inputs.md
new file mode 100644
index 0000000..f5a905e
--- /dev/null
+++ b/docs/components/form-inputs.md
@@ -0,0 +1,192 @@
+# Form Inputs
+
+All input components extend a shared `InputBase` class that provides consistent styling, htmx validation integration, and parameter unification. When placed inside an `HtmxForm` with a `FormField`, inputs automatically attach `hx-post`, `hx-trigger="blur"`, and `hx-target` attributes for real-time per-field validation.
+
+---
+
+## TextInput
+
+A standard text input for strings. Supports all HTML input types (`text`, `email`, `password`, `search`, etc.).
+
+```razor
+
+
+
+
+
+
+
+
+
+
+
+```
+
+### Parameters
+
+| Parameter | Type | Default | Description |
+|---|---|---|---|
+| `Type` | `string` | `"text"` | HTML input type |
+| `Id` | `string?` | — | Input element ID |
+| `Name` | `string?` | — | Form field name (submitted to the server) |
+| `Value` | `string?` | — | Current value |
+| `Placeholder` | `string?` | — | Placeholder text |
+| `Disabled` | `bool` | `false` | Disables the input |
+| `ReadOnly` | `bool` | `false` | Makes the input read-only |
+| `Class` | `string?` | — | Additional CSS classes |
+
+---
+
+## NumberInput
+
+A numeric input with built-in increment/decrement stepper buttons. Hides the browser's native spinner.
+
+```razor
+
+
+
+
+
+
+
+```
+
+### Parameters
+
+| Parameter | Type | Default | Description |
+|---|---|---|---|
+| `Min` | `string?` | — | Minimum allowed value |
+| `Max` | `string?` | — | Maximum allowed value |
+| `Step` | `string?` | — | Step increment |
+| `Value` | `double?` | — | Current numeric value |
+
+Inherits all parameters from `InputBase` (`Id`, `Name`, `Placeholder`, `Disabled`, `ReadOnly`, `Class`).
+
+The stepper buttons are disabled when the value reaches `Min` or `Max`. They are hidden when `Disabled` or `ReadOnly` is true.
+
+---
+
+## DateInput
+
+A date picker that combines a hidden `` with a popover calendar. Users select dates through the calendar UI — the native date picker chrome is hidden.
+
+```razor
+
+
+
+```
+
+### How it works
+
+1. A hidden `` holds the actual form value in `yyyy-MM-dd` format
+2. A styled button trigger shows the selected date (or placeholder)
+3. Clicking the trigger opens a `Popover` containing a `Calendar` component
+4. Selecting a day updates the hidden input and closes the popover
+
+### Parameters
+
+| Parameter | Type | Default | Description |
+|---|---|---|---|
+| `Value` | `DateOnly?` | — | Currently selected date |
+| `Min` | `string?` | — | Minimum date |
+| `Max` | `string?` | — | Maximum date |
+| `Placeholder` | `string?` | `"Select date"` | Trigger button placeholder |
+
+Inherits all parameters from `InputBase`.
+
+---
+
+## TimeInput
+
+A time picker that combines a hidden `` with a popover time picker. Features scrollable hour/minute columns and AM/PM toggle.
+
+```razor
+
+
+
+```
+
+### Parameters
+
+| Parameter | Type | Default | Description |
+|---|---|---|---|
+| `Value` | `TimeOnly?` | — | Currently selected time |
+| `Step` | `string?` | — | Minute step interval |
+| `Use12Hour` | `bool` | `true` | 12-hour format with AM/PM |
+| `Placeholder` | `string?` | `"Select time"` | Trigger button placeholder |
+
+Inherits all parameters from `InputBase`.
+
+---
+
+## DateTimeInput
+
+Combines a date picker and time picker side by side for selecting both date and time. Internally manages a hidden `` plus two helper hidden inputs for the date and time parts.
+
+```razor
+
+
+
+```
+
+### How it works
+
+1. A hidden `` holds the combined value in `yyyy-MM-ddTHH:mm` format
+2. Two helper hidden inputs hold the date-part and time-part separately
+3. Two popover triggers (calendar + time picker) are displayed side by side
+4. The `forms.js` module automatically combines the date-part and time-part values into the main hidden input
+
+### Parameters
+
+| Parameter | Type | Default | Description |
+|---|---|---|---|
+| `Value` | `DateTime?` | — | Currently selected date and time |
+| `Min` | `string?` | — | Minimum datetime |
+| `Max` | `string?` | — | Maximum datetime |
+| `Step` | `string?` | — | Minute step interval |
+| `Placeholder` | `string?` | `"Select date"` | Date trigger placeholder |
+
+Inherits all parameters from `InputBase`.
+
+---
+
+## FormField
+
+Wraps an input with a label, description, and error placeholder. The error element is used by htmx to display server-side validation messages.
+
+```razor
+
+
+
+```
+
+### Parameters
+
+| Parameter | Type | Default | Description |
+|---|---|---|---|
+| `Label` | `string?` | — | Label text rendered as `