# Tailwind CSS for Flutter Documentation (Full)
> Consolidated documentation for context window loading.
---
# Installation
# Installation
- [Requirements](#requirements)
- [Installation](#installation-step)
- [Basic Setup](#basic-setup)
- [Alternative Setup](#alternative-setup)
- [Verify Installation](#verify-installation)
Getting started with Wind requires a few configuration steps to ensure utility classes resolve correctly across your application.
## Requirements
Before adding Wind to your project, ensure your environment meets these minimum version requirements. We recommend staying on the latest stable versions for the best experience.
| Dependency | Minimum Version | Recommended |
|:-----------|:----------------|:------------|
| Flutter | `>= 3.27.0` | `3.27.0+` |
| Dart | `>= 3.4.0` | `3.6.0+` |
## Installation
Add `fluttersdk_wind` to your `pubspec.yaml` using the Flutter CLI:
```bash
flutter pub add fluttersdk_wind
```
Alternatively, add it manually to your dependencies:
```yaml
dependencies:
fluttersdk_wind: any # or pin to latest stable from pub.dev/packages/fluttersdk_wind
```
## Basic Setup
To use Wind utilities, you must wrap your application with the `WindTheme` widget. This provides the context needed for responsive breakpoints, dark mode, and state resolution.
### The Builder Pattern (Recommended)
The `builder` pattern is the preferred way to initialize Wind. It provides a `controller` that synchronizes Wind's theme state (like dark mode) directly with your `MaterialApp`.
Let's look at a typical implementation:
```dart
import 'package:flutter/material.dart';
import 'package:fluttersdk_wind/fluttersdk_wind.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return WindTheme(
// Always use the 'data' parameter for configuration
data: WindThemeData(),
builder: (context, controller) {
return MaterialApp(
title: 'Wind App',
// Automatically syncs Material theme with Wind's state
theme: controller.toThemeData(),
home: const HomePage(),
);
},
);
}
}
```
By using the `builder` pattern, any theme changes triggered via `context.windTheme.toggleTheme()` will instantly propagate to both Wind widgets and native Material components.
> [!NOTE]
> Ensure you use the `data:` parameter. The `theme:` parameter found in earlier versions is deprecated and will result in compilation errors in v1.
## Alternative Setup
If you do not need reactive synchronization with `MaterialApp` or are only using Wind in a specific subtree, you can use the `child` pattern:
```dart
WindTheme(
data: WindThemeData(),
child: MaterialApp(
home: const HomePage(),
),
)
```
While this setup works for Wind widgets like `WDiv` or `WText`, native Material widgets will not automatically react to Wind theme changes unless handled manually.
## Verify Installation
To verify that Wind is correctly configured, add a `WDiv` with a few utility classes to your project:
```dart
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: WDiv(
className: 'bg-blue-500 p-6 rounded-xl shadow-lg',
child: WText(
'Wind is working!',
className: 'text-white font-bold text-xl',
),
),
),
);
}
}
```
If you see a blue card with white text, the installation is successful.
---
# Utility-First Fundamentals
# Utility-First Fundamentals
Utility-first styling is the core philosophy of Wind. Instead of building complex, nested widget trees for every design detail, you apply pre-defined utility classes directly to your widgets to compose styles.
- [Introduction](#introduction)
- [The Shift in Thinking](#the-shift-in-thinking)
- [Why Utility-First?](#why-utility-first)
- [How it Works](#how-it-works)
- [Quick Reference](#quick-reference)
- [Syntax Guide](#syntax-guide)
## Introduction
Wind translates utility strings into high-performance Flutter widget trees. This approach moves styling from the "implementation" phase into the "composition" phase, allowing you to build UIs by combining functional building blocks.
```dart
WDiv(
className: 'flex flex-col gap-4 p-6 bg-white rounded-xl shadow-lg border border-gray-100',
children: [
WText('Utility-First', className: 'text-2xl font-bold text-blue-600'),
WText('Style your Flutter apps with ease.', className: 'text-gray-500'),
WButton(
className: 'bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg',
child: Text('Get Started'),
),
],
)
```
## The Shift in Thinking
Traditionally, styling in Flutter involves deep nesting. To create a simple card with padding and a shadow, you typically wrap widgets inside multiple containers and decorators.
Let's compare the two approaches:
### Traditional Flutter
```dart
// Standard Flutter approach
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: const Text(
"Hello World",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
)
```
### Wind
```dart
// Wind approach
WDiv(
className: "p-4 bg-white rounded-xl shadow-lg",
child: WText(
"Hello World",
className: "text-lg font-bold",
),
)
```
By flattening the widget tree, Wind makes the UI structure more readable and significantly reduces the amount of code required to build complex interfaces.
## Why Utility-First?
Adopting a utility-first workflow provides several architectural advantages:
- **Development Velocity**: Stay within your widget tree. There is no need to jump between separate files or long constructor lists to adjust styling.
- **Design Consistency**: Instead of using magic numbers, you work with a standardized scale (e.g., `p-4` for 16px). This ensures your UI remains visually aligned.
- **Improved Maintainability**: Styles are local to the widget. When you modify a `WDiv`, you see exactly what it affects without worrying about global CSS-like side effects.
- **Declarative Modifiers**: Handling responsiveness (`md:`), dark mode (`dark:`), and states (`hover:`) is handled via simple prefixes rather than conditional logic in your build methods.
## How it Works
Wind uses a high-performance pipeline to transform strings into native Flutter styles. This process is optimized through caching to ensure runtime overhead is minimal.
1. **Context Initialization**: `WindContext` captures the current environment, including screen size (breakpoints), brightness, and theme scales.
2. **Parsing**: The `WindParser` tokenizes the `className` string and delegates to specialized parsers for colors, spacing, borders, and more.
3. **Style Composition**: Parsers generate a `WindStyle` object—an immutable, typed representation of the requested styles.
4. **Widget Application**: `W-prefixed` widgets consume this `WindStyle` to dynamically build the optimal Flutter widget hierarchy (e.g., injecting `Padding` or `DecoratedBox` only when needed).
> [!NOTE]
> Wind implements an LRU (Least Recently Used) cache. Once a class string is parsed, subsequent renders retrieve the pre-computed style almost instantly.
## Quick Reference
Common utility categories and their primary classes:
| Category | Primary Classes | Description |
|:---------|:----------------|:------------|
| **Layout** | `flex`, `grid`, `block`, `hidden` | Controls positioning and visibility. |
| **Spacing** | `p-4`, `m-2`, `gap-4`, `px-6` | Manages padding, margins, and spacing between children. |
| **Sizing** | `w-full`, `h-64`, `max-w-md` | Defines explicit widths and heights. |
| **Colors** | `bg-blue-500`, `text-white` | Applies theme colors to backgrounds and text. |
| **Borders** | `border`, `rounded-lg`, `ring-2` | Configures borders, radii, and focus rings. |
## Syntax Guide
Wind uses a syntax that matches Tailwind CSS, supporting standard values, arbitrary inputs, and modifiers.
| Pattern | Example | Description |
|:--------|:--------|:------------|
| **Standard** | `p-4` | A predefined value from your theme scale. |
| **Negative** | `-m-4` | Inverts a value (primarily for margins). |
| **Arbitrary** | `w-[350px]` | Uses exact values inside square brackets for one-off styles. |
| **Opacity** | `bg-blue-500/50` | Appends a percentage to a color to set alpha transparency. |
| **Modifiers** | `md:flex-row` | Conditionally applies styles based on screen size or state. |
---
# Theme Configuration
# Theme Configuration
- [Introduction](#introduction)
- [The WindTheme Widget](#the-windtheme-widget)
- [Theme Change Callbacks](#theme-change-callbacks)
- [Resetting to System Theme](#resetting-to-system-theme)
- [Colors](#colors)
- [Typography](#typography)
- [Spacing and Sizing](#spacing-and-sizing)
- [Borders and Shadows](#borders-and-shadows)
- [Customizing Defaults](#customizing-defaults)
- [Quick Reference](#quick-reference)
## Introduction
Wind is designed to be fully customizable from the ground up. If you've ever used Tailwind CSS, you'll feel right at home with how we handle "design tokens." Instead of hunting through nested widget properties to change a color or a font size, you define your design system once in your theme configuration.
Everything in Wind—from the colors in `bg-blue-500` to the spacing in `p-4`—is driven by `WindThemeData`. This ensures that your UI remains consistent across the entire application while giving you the freedom to break away from defaults whenever you need to.
## The WindTheme Widget
To start customizing your design system, you need to wrap your application with the `WindTheme` widget. This widget acts as a provider, making your theme configuration available to all `W` widgets in the tree.
> [!IMPORTANT]
> Always use the `data:` parameter when providing your theme configuration. Earlier alpha versions used `theme:`, but this has been updated for consistency.
Let's look at a basic setup:
```dart
import 'package:fluttersdk_wind/fluttersdk_wind.dart';
void main() {
runApp(
WindTheme(
data: WindThemeData(
// We'll customize this in the next sections
),
onThemeChanged: (brightness) {
// Optional: persist user's theme preference
},
child: MaterialApp(
home: MyHome(),
),
),
);
}
```
## Theme Change Callbacks
When a user manually toggles the theme via `toggleTheme()`, you may want to persist their preference. The `onThemeChanged` callback fires only on user-initiated theme changes—it does **not** fire when the system brightness changes automatically.
```dart
WindTheme(
onThemeChanged: (brightness) {
// Persist user preference
Vault.set('theme_mode', brightness == Brightness.dark ? 'dark' : 'light');
},
data: WindThemeData(),
child: MaterialApp(
home: MyHome(),
),
)
```
> [!IMPORTANT]
> `onThemeChanged` only fires when the user calls `toggleTheme()`. Automatic system brightness sync does not trigger this callback.
## Resetting to System Theme
After a user manually toggles the theme, the automatic system sync is disabled to respect their choice. To re-enable automatic system brightness sync, call `resetToSystem()`:
```dart
// Re-enable system brightness sync
WindTheme.of(context).resetToSystem();
// Or via extension
context.windTheme.resetToSystem();
```
This immediately syncs the theme with the current platform brightness and re-enables the `syncWithSystem` flag so future OS changes are reflected automatically.
## Colors
Colors in Wind are defined as palettes. When you add a color to your theme, Wind expects a `MaterialColor` (or a Map of shades) so it can resolve utilities like `bg-brand-50` through `bg-brand-950`.
Here's how to define custom colors:
```dart
WindThemeData(
colors: {
// Single color - Wind automatically generates shades
'brand': Colors.indigo,
// Explicit shades for fine-tuned control
'accent': MaterialColor(0xFF3B82F6, {
50: Color(0xFFEFF6FF),
500: Color(0xFF3B82F6),
900: Color(0xFF1E3A8A),
}),
},
)
```
Once defined, these become available as utility classes immediately: `text-brand-600`, `bg-accent-500/50`, or `border-brand-900`.
## Typography
You can take full control of your app's typography by overriding font families, sizes, and weights. Wind applies a "sans" font family globally by default, mimicking Tailwind's behavior.
```dart
WindThemeData(
fontFamilies: {
'sans': 'Inter', // The default global font
'display': 'Oswald', // Accessible via font-display
},
fontSizes: {
'xs': 12.0,
'base': 16.0,
'xl': 20.0,
'4xl': 36.0,
},
fontWeights: {
'thin': FontWeight.w100,
'bold': FontWeight.w700,
},
)
```
But what if you don't want Wind to inject a global `DefaultTextStyle`? You can simply set `applyDefaultFontFamily: false` in your configuration.
## Spacing and Sizing
Wind uses a numeric scale for spacing (`p-4`, `m-2`, `gap-6`). By default, each unit is equal to `4.0` logical pixels. This means `p-4` translates to `16.0` pixels.
If your design system is built on a different grid (like a 5px or 8px grid), you can adjust the `baseSpacingUnit`:
```dart
WindThemeData(
baseSpacingUnit: 5.0, // Now p-4 = 20px
)
```
You can also customize the "container" sizes or "screen" breakpoints if the default Tailwind values don't fit your needs:
```dart
WindThemeData(
screens: {
'sm': 600,
'md': 900,
'lg': 1200,
},
)
```
## Borders and Shadows
The "feel" of your app often comes down to its borders and shadows. Wind allows you to define these scales explicitly so your `rounded-lg` or `shadow-xl` classes are always consistent.
```dart
WindThemeData(
borderRadius: {
'none': 0,
'sm': 2,
'DEFAULT': 4, // Applied by 'rounded' class
'lg': 8,
'full': 9999,
},
shadows: {
'sm': [BoxShadow(color: Colors.black12, blurRadius: 2)],
'md': [BoxShadow(color: Colors.black26, blurRadius: 6, offset: Offset(0, 2))],
},
)
```
## Customizing Defaults
Wind provides a comprehensive default theme that matches Tailwind CSS v3. When you provide your own `WindThemeData`, your values are **merged** with the defaults.
If you want to tweak just one or two things from an existing configuration, use `copyWith`:
```dart
final darkTheme = myDefaultTheme.copyWith(
brightness: Brightness.dark,
ringColor: Colors.amber,
);
```
## Quick Reference
`WindThemeData` exposes 23 configurable fields. The table below groups them by concern.
### Mode and Behavior
| Property | Type | Default | Description |
|:---------|:-----|:--------|:------------|
| `brightness` | `Brightness` | `light` | Initial mode (`light` or `dark`) |
| `syncWithSystem` | `bool` | `true` | Auto-follow OS brightness until the user calls `toggleTheme()` |
| `applyDefaultFontFamily` | `bool` | `true` | Inject Wind's default font family as a global `DefaultTextStyle` |
| `baseSpacingUnit` | `double` | `4.0` | Multiplier for numeric spacing (`p-4` → `4 * 4 = 16px`) |
### Tokens
| Property | Type | Description |
|:---------|:-----|:------------|
| `colors` | `Map` | Custom color palettes (`bg-brand-500`, etc.) |
| `screens` | `Map` | Breakpoint min-widths (`sm`, `md`, `lg`, `xl`, `2xl`, custom) |
| `containers` | `Map` | Container max-widths (`container` utility) |
| `fontFamilies` | `Map` | Font aliases (`sans`, `serif`, `mono`, custom) |
| `fontSizes` | `Map` | Text size scale (`text-xs` through `text-6xl`) |
| `fontWeights` | `Map` | Weight scale (`font-thin` through `font-black`) |
| `tracking` | `Map` | Letter spacing (`tracking-tight`, etc.) |
| `leading` | `Map` | Line height (`leading-none`, etc.) |
| `borderWidths` | `Map` | Border width scale (`border-2`, etc.) |
| `borderRadius` | `Map` | Corner radius scale (`rounded-lg`, etc.) |
| `shadows` | `Map>` | Shadow definitions (`shadow-md`, etc.) |
| `opacities` | `Map` | Opacity scale (`opacity-50`, etc.) |
| `zIndices` | `Map` | Z-index scale (`z-10`, etc.) |
| `transitionDurations` | `Map` | Duration scale (`duration-200`, etc.) |
| `transitionCurves` | `Map` | Easing scale (`ease-in-out`, etc.) |
| `animations` | `Map` | Animation types (`animate-spin`, etc.) |
### Ring (Focus) Effects
| Property | Type | Default | Description |
|:---------|:-----|:--------|:------------|
| `ringColor` | `Color` | Tailwind blue-500 | Default ring color when not overridden by `ring-{color}` |
| `ringWidths` | `Map` | `0,1,2,4,8` | Ring width scale (`ring-2`, etc.) |
| `ringOffsets` | `Map` | `0,1,2,4,8` | Ring offset scale (`ring-offset-2`, etc.) |
### Widget-level callback
`onThemeChanged` is a `WindTheme` widget parameter, not a `WindThemeData` field. It fires on user-initiated `toggleTheme()` calls and is documented in the [Theme Change Callbacks](#theme-change-callbacks) section above.
For more details on how to sync these values with Flutter's standard Material components, check out the [Theme Binding](./theme-binding.md) guide.
---
# Theme Binding
# Theme Binding
Theme binding allows you to sync your Wind configuration with Flutter's native `ThemeData` system. This ensures that standard Material widgets automatically reflect your Wind tokens, creating a cohesive visual experience across your entire application.
- [Why Bind Themes?](#why-bind-themes)
- [Reactive Binding](#reactive-binding)
- [Toggling Themes](#toggling-themes)
- [Static Binding](#static-binding)
- [Mapping Reference](#mapping-reference)
## Why Bind Themes?
By default, Wind manages its own styling tokens (colors, spacing, typography). However, Flutter's built-in widgets (like `AppBar`, `FloatingActionButton`, or `TextField`) rely on the standard `Theme.of(context)` to determine their appearance.
Binding the two systems ensures:
- **Consistency:** A button using `bg-primary-500` matches a standard `ElevatedButton`.
- **Inheritance:** Standard Flutter widgets automatically pick up your Wind font families and colors.
- **Automation:** Toggling dark mode in Wind automatically updates the `brightness` of your standard Material theme.
## Reactive Binding
To ensure your entire application rebuilds when the theme changes (e.g., switching to dark mode), use the `builder` pattern. This provides a `WindController` that dynamically generates a new `ThemeData` whenever the state updates.
```dart
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final windTheme = WindThemeData(
colors: {
'primary': Colors.indigo,
'secondary': Colors.teal,
},
);
return WindTheme(
data: windTheme,
builder: (context, controller) {
// controller.toThemeData() produces a standard Flutter ThemeData
return MaterialApp(
theme: controller.toThemeData(),
home: const HomePage(),
);
},
);
}
}
```
## Toggling Themes
When using the reactive `builder` pattern, you can trigger a global theme update from anywhere in the widget tree. This will automatically update both Wind utilities and standard Material widgets.
```dart
// Toggle between Light and Dark mode
context.windTheme.toggleTheme();
```
## Static Binding
If you do not need dynamic theme switching at runtime, you can bind the theme once during initialization. This is useful for apps with a fixed brand identity or forced dark/light modes.
```dart
final windTheme = WindThemeData(
brightness: Brightness.dark,
colors: {'primary': Colors.purple},
);
return WindTheme(
data: windTheme,
child: MaterialApp(
theme: windTheme.toThemeData(),
home: const HomePage(),
),
);
```
## Mapping Reference
The `toThemeData()` method intelligently maps Wind tokens to their standard Flutter counterparts:
| Wind Token | Flutter Theme Property |
|:-----------|:-----------------------|
| `colors['primary']` | `colorScheme.primary` |
| `colors['secondary']` | `colorScheme.secondary` |
| `colors['error']` | `colorScheme.error` |
| `colors['background']` | `scaffoldBackgroundColor` |
| `brightness` | `brightness` |
| `fontFamilies['sans']` | `textTheme.fontFamily` |
This mapping ensures that your utility-first styles and native widgets stay perfectly in sync.
---
# Responsive Design
# Responsive Design
Building interfaces that adapt to any screen size is a core requirement for modern applications. Wind makes this process declarative using a mobile-first, utility-based approach.
- [Mobile-First Approach](#mobile-first-approach)
- [Breakpoint Reference](#breakpoint-reference)
- [Platform Prefixes](#platform-prefixes)
- [Combining Modifiers](#combining-modifiers)
- [Customizing Breakpoints](#customizing-breakpoints)
```dart
// Stacked on mobile (default), horizontal on medium screens and up
WDiv(
className: 'flex flex-col md:flex-row gap-4 p-4 bg-gray-100 md:bg-blue-50',
children: [
WDiv(className: 'w-full md:w-1/2 h-24 bg-blue-500'),
WDiv(className: 'w-full md:w-1/2 h-24 bg-red-500'),
],
)
```
## Mobile-First Approach
Wind follows a mobile-first philosophy. This means that utility classes without a prefix apply to all screen sizes by default. Responsive modifiers like `md:` or `lg:` only apply when the screen width meets or exceeds the specified breakpoint.
Consider this example:
```dart
// Mobile: blue background, Desktop: green background
WDiv(className: 'bg-blue-500 lg:bg-green-500')
```
In this case, the `bg-blue-500` class is the base style. The `lg:bg-green-500` class only activates when the screen width is at least 1024px, overriding the base background color.
## Breakpoint Reference
Wind includes five default breakpoints inspired by the standard Tailwind CSS scale.
| Prefix | Value | Description |
|:-------|:------|:------------|
| `sm` | 640px | Large phones / Small tablets |
| `md` | 768px | Tablets (Portrait) |
| `lg` | 1024px | Laptops / Tablets (Landscape) |
| `xl` | 1280px | Desktops |
| `2xl` | 1536px | Large Desktops |
> [!NOTE]
> These values represent the **minimum width** required for the modifier to take effect.
## Platform Prefixes
In addition to screen sizes, Wind supports platform-specific modifiers. These allow you to apply styles based on the operating system or environment.
| Prefix | Applies To |
|:-------|:-----------|
| `ios:` | iOS devices |
| `android:` | Android devices |
| `macos:` | macOS |
| `web:` | Web browsers |
| `mobile:` | Both iOS and Android |
| `windows:` | Windows |
| `linux:` | Linux |
Example usage:
```dart
WDiv(
className: 'p-4 ios:p-6 android:p-2 web:p-8',
child: WText('Platform-specific padding'),
)
```
## Combining Modifiers
You can chain multiple modifiers to create highly specific styling rules. For example, you might want a hover effect that only applies in dark mode on large screens.
```dart
WButton(
className: 'bg-blue-500 lg:dark:hover:bg-indigo-600',
child: WText('Submit'),
)
```
> [!WARNING]
> Modifier order is strict. Breakpoint modifiers must always come first in the chain (e.g., `lg:dark:hover:`).
## Customizing Breakpoints
If the default breakpoints do not match your project requirements, you can define custom screens in `WindThemeData`.
```dart
WindTheme(
data: WindThemeData(
screens: {
'tablet': 600,
'laptop': 900,
'desktop': 1200,
},
),
child: MyApp(),
)
```
Once defined, these custom keys can be used as prefixes: `tablet:p-4`, `laptop:flex-row`, etc.
## Related Documentation
- [Utility-First Fundamentals](./utility-first.md)
- [Theming](./theming.md)
- [Dark Mode](./dark-mode.md)
- [State Management](./state-management.md)
---
# Dark Mode
# Dark Mode
Wind makes it incredibly easy to support dark mode using the `dark:` variant modifier. Instead of managing complex theme objects or multiple style sheets, you simply prefix your utility classes to apply styles only when dark mode is active.
- [Basic Usage](#basic-usage)
- [System vs Manual Mode](#system-vs-manual-mode)
- [Responsive Dark Mode](#responsive-dark-mode)
- [Best Practices](#best-practices)
```dart
WDiv(
className: "p-6 bg-white dark:bg-slate-900 rounded-xl shadow-lg dark:shadow-none border border-gray-200 dark:border-slate-800",
child: WText(
"Dark Mode Support",
className: "text-gray-900 dark:text-white font-bold",
),
)
```
## Basic Usage
To style an element for dark mode, add the `dark:` prefix to any utility class. This works for colors, borders, shadows, and even opacity.
Let's look at a practical example:
```dart
WDiv(
className: "bg-gray-100 text-gray-900 dark:bg-gray-800 dark:text-gray-100",
child: WText("Adaptive text colors"),
)
```
In this example, the background will be light gray in light mode and shift to a dark gray when dark mode is enabled. The text color behaves similarly, maintaining readability across themes.
## System vs Manual Mode
By default, Wind follows the system's brightness settings via `MediaQuery.platformBrightness`. However, you can manually override this behavior to allow users to toggle themes within your app.
### Manual Toggling
You can use the `WindTheme` controller to toggle the theme or set a specific brightness programmatically:
```dart
// Toggle between light and dark
context.windTheme.toggleTheme();
// Force dark mode
context.windTheme.setTheme(
context.windThemeData.copyWith(brightness: Brightness.dark),
);
```
### Material Sync
If you want your Material `ThemeData` to stay in sync with Wind's dark mode, use the `toThemeData()` helper in your `MaterialApp`. This is particularly useful for styling native Material components that aren't yet wrapped in Wind widgets.
```dart
WindTheme(
data: WindThemeData(),
builder: (context, controller) => MaterialApp(
theme: controller.toThemeData(),
home: const HomePage(),
),
)
```
## Responsive Dark Mode
You can combine the `dark:` modifier with responsive breakpoints to create highly specific layouts. The order of modifiers doesn't strictly matter, but following a consistent pattern like `{breakpoint}:{state}:{utility}` is recommended for readability.
```dart
WDiv(
// Blue background on mobile, but red on large screens ONLY in dark mode
className: "bg-blue-500 lg:dark:bg-red-500",
)
```
## Best Practices
### Avoid Pure Black
Using pure black (`#000000`) for backgrounds can often lead to high contrast that causes eye strain. We recommend using deep grays or slates like `slate-900` or `gray-900` for a more polished dark mode experience.
```dart
// Recommended
className: "dark:bg-slate-900"
// Use sparingly
className: "dark:bg-black"
```
### Soften Your Borders
Borders that look great in light mode might appear too harsh in dark mode. Consider reducing the opacity or choosing a darker shade for borders to keep the design soft.
```dart
WDiv(className: "border border-gray-200 dark:border-gray-700")
```
---
# State Management & Interactive Styles
# State Management & Interactive Styles
- [Introduction](#introduction)
- [How It Works](#how-it-works)
- [Built-in State Prefixes](#built-in-state-prefixes)
- [Quick Reference](#quick-reference)
- [Interactive Widgets](#interactive-widgets)
- [WAnchor: The Core Wrapper](#wanchor-the-core-wrapper)
- [WButton: The High-Level Action](#wbutton-the-high-level-action)
- [WInput: Focus States](#winput-focus-states)
- [Custom States](#custom-states)
- [State Propagation](#state-propagation)
- [Related Documentation](#related-documentation)
## Introduction
Interactive styles allow you to define how your UI reacts to user input without managing complex state variables. Instead of nesting multiple `MouseRegion`, `GestureDetector`, and `Focus` widgets, Wind provides a declarative way to handle states directly in your `className` string.
By using prefixes like `hover:`, `focus:`, and `active:`, you can build responsive, tactile interfaces that feel native to both web and mobile platforms.
## How It Works
Wind's parsing engine identifies state prefixes in your class string and stores them in a separate style map. When the widget's internal state changes (e.g., a user hovers over it), the widget rebuilds and applies the corresponding styles.
Consider this basic button example:
```dart
WButton(
onTap: () => print('Action triggered'),
className: 'bg-blue-500 hover:bg-blue-600 active:bg-blue-700 p-4 rounded-lg duration-200',
child: WText('Save Changes', className: 'text-white'),
)
```
In this case, the background color transitions smoothly between shades based on the interaction state.
> [!NOTE]
> For state prefixes to work, the widget must be wrapped in an interactive component like `WAnchor`, `WButton`, or `WInput`. A standard `WDiv` is passive and will not trigger interaction states on its own.
## Built-in State Prefixes
Wind supports several interactive states out of the box. These are automatically managed when using interactive widgets:
- **hover**: Triggered when a pointer enters the widget bounds.
- **focus**: Triggered when the widget receives keyboard or input focus.
- **active**: Triggered while the widget is being pressed or tapped.
- **disabled**: Triggered when the widget's interactivity is disabled (e.g., `onTap` is null).
## Quick Reference
| Prefix | Trigger | Typical Use Case |
|:-------|:--------|:-----------------|
| `hover:` | Pointer entry | Changing background or elevation on desktop. |
| `focus:` | Keyboard focus | Showing a ring or border on input fields. |
| `active:` | Pointer press | Scaling down or darkening a button on tap. |
| `disabled:` | Interactivity off | Reducing opacity or greying out actions. |
## Interactive Widgets
Wind provides specific widgets designed to manage and propagate these states.
### WAnchor: The Core Wrapper
`WAnchor` is the low-level engine behind Wind's interactivity. It provides no visual styling itself; its sole purpose is to detect gestures and share that state with its child tree. This is useful for making entire cards or complex layouts interactive.
```dart
WAnchor(
onTap: () {},
child: WDiv(
className: 'bg-white hover:bg-slate-50 border p-6 rounded-xl duration-300',
children: [
WText('Interactive Card', className: 'group-hover:text-blue-600'),
],
),
)
```
### WButton: The High-Level Action
While `WAnchor` is for generic wrappers, `WButton` is optimized for actions. It includes built-in support for loading states, padding defaults, and button-specific accessibility.
```dart
WButton(
onTap: _submit,
className: 'bg-indigo-600 hover:bg-indigo-700 disabled:opacity-50 text-white px-8 py-3 rounded',
child: Text('Submit'),
)
```
### WInput: Focus States
`WInput` manages its own focus state. It is common to use `focus:` prefixes here to highlight the active field or show a focus ring.
```dart
WInput(
placeholder: 'Search...',
className: 'border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-500/20 p-3 rounded-lg',
)
```
## Custom States
Sometimes you need to style a widget based on a state that isn't built-in, such as whether an item is "selected" or "loading". You can pass custom strings to the `states` parameter.
```dart
WDiv(
className: 'border-2 border-gray-200 selected:border-blue-500 bg-white selected:bg-blue-50 p-4',
states: isSelected ? {'selected'} : {},
child: WText('Selection Item'),
)
```
If the `states` set contains "selected", any class prefixed with `selected:` will be applied.
## State Propagation
Interactivity in Wind is shared through `WindContext`. When an ancestor widget like `WAnchor` changes state, it propagates that state to all children in its subtree.
Let's look at a nested example:
```dart
WAnchor(
onTap: () {},
child: WDiv(
className: 'p-4 bg-white hover:bg-gray-50',
children: [
WText('Title', className: 'font-bold hover:text-blue-600'),
WText('Description', className: 'text-gray-500'),
],
),
)
```
Even if the mouse is only hovering over the padding of the `WDiv`, the `WText` widget will receive the "hover" state and update its color accordingly.
## Related Documentation
- [Responsive Design](./responsive-design.md) - Combining states with breakpoints.
- [Dark Mode](./dark-mode.md) - Styling for night owls.
- [WAnchor Widget](../widgets/w-anchor.md) - Technical reference for the anchor engine.
---
# Dynamic Rendering
# Dynamic Rendering
Build Flutter UIs from JSON at runtime. WDynamic turns a Map structure into a live widget tree with action handling and form state management.
- [Introduction](#introduction)
- [How It Works](#how-it-works)
- [JSON Schema](#json-schema)
- [Action System](#action-system)
- [Form State Management](#form-state-management)
- [Security & Whitelisting](#security-whitelisting)
- [Custom Widget Builders](#custom-widget-builders)
- [Custom Icons](#custom-icons)
- [Error Handling](#error-handling)
- [Related Documentation](#related-documentation)
## Introduction
Dynamic rendering, also known as Server-Driven UI, allows your backend to control the structure and behavior of your Flutter interface at runtime. Instead of hard-coding widget trees in Dart, you define them in JSON and let WDynamic convert them into live widgets.
This approach unlocks powerful capabilities:
- **CMS-driven layouts** - Non-technical teams can author UI configurations through admin panels.
- **A/B testing** - Experiment with different layouts without rebuilding your app.
- **Remote configuration** - Update UI elements instantly via API responses.
- **White-label apps** - Customize entire interfaces per client from a single codebase.
**Traditional approach:**
```dart
// Hard-coded widget tree
WDiv(
className: 'flex gap-4 p-6',
children: [
WButton(onTap: handleSubmit, child: WText('Submit')),
],
)
```
**Dynamic approach:**
```dart
// JSON-driven widget tree
WDynamic(
json: {
'type': 'WDiv',
'props': {'className': 'flex gap-4 p-6'},
'children': [
{
'type': 'WButton',
'props': {
'onTap': {'action': 'handleSubmit'},
},
'children': [
{'type': 'WText', 'props': {'text': 'Submit'}}
],
},
],
},
actions: {'handleSubmit': (args, state) => print('Form: ${state.getAll()}')},
)
```
## How It Works
WDynamic follows a simple pipeline:
```
JSON Map → WDynamicRenderer → Widget Tree
```
The renderer parses your JSON configuration, validates widget types against a security whitelist, instantiates the corresponding Flutter widgets, and wires up action callbacks. All Wind utilities (className, responsive prefixes, state modifiers) work exactly as they do in statically-defined widgets.
Here's a basic example that renders a styled container with text:
```dart
WDynamic(
json: {
'type': 'WDiv',
'props': {
'className': 'p-6 bg-white dark:bg-slate-800 rounded-xl shadow-sm',
},
'children': [
{
'type': 'WText',
'props': {
'text': 'Hello from JSON!',
'className': 'text-xl font-bold text-gray-800 dark:text-white',
},
},
],
},
)
```
The output is identical to writing the equivalent `WDiv` and `WText` widgets directly in Dart.
## JSON Schema
Every widget definition follows a consistent three-key structure:
```json
{
"type": "WidgetType",
"props": { /* widget properties */ },
"children": [ /* nested widgets */ ]
}
```
### type
The widget class name as a string. Must be present in the default whitelist or registered via custom builders.
```dart
{'type': 'WDiv'}
{'type': 'WButton'}
{'type': 'Column'} // Flutter core widgets also supported
```
### props
A map of properties specific to the widget type. Common properties include:
| Property | Description | Example |
|:---------|:------------|:--------|
| `className` | Wind utility classes | `"flex gap-4 p-6"` |
| `text` | Text content (WText) | `"Submit Form"` |
| `placeholder` | Input placeholder (WInput) | `"Enter email..."` |
| `id` | State tracking identifier | `"username"` |
| `onTap`, `onChange`, etc. | Action bindings | `{"action": "submit", "args": {...}}` |
```dart
{
'type': 'WInput',
'props': {
'id': 'email',
'placeholder': 'user@example.com',
'className': 'border border-gray-300 rounded-lg p-3',
'onChange': {'action': 'validateEmail'},
},
}
```
### children
An array of nested widget definitions. Optional for widgets like `WText` or `WIcon`.
```dart
{
'type': 'WDiv',
'props': {'className': 'flex flex-col gap-2'},
'children': [
{'type': 'WText', 'props': {'text': 'First'}},
{'type': 'WText', 'props': {'text': 'Second'}},
],
}
```
## Action System
Interactive widgets (buttons, inputs, checkboxes) trigger actions via props like `onTap`, `onLongPress`, `onDoubleTap`, `onChange`, and `onChanged`. Each action prop references a named handler registered in the `actions` map.
**Supported action props:**
- `onTap` - Single tap/click (WButton, WAnchor)
- `onLongPress` - Long press gesture (WButton, WAnchor)
- `onDoubleTap` - Double tap/click (WButton, WAnchor)
- `onChange` - Value change (WInput, WCheckbox, WSelect, WDatePicker)
- `onChanged` - Alias for onChange
**JSON syntax:**
```dart
{
'onTap': {
'action': 'actionName',
'args': {'key': 'value'}
}
}
```
**Handler signature:**
Action handlers support two signatures. Wind automatically detects which one you provide:
```dart
// Simple signature (args only)
(Map args) {
print('Action triggered with ${args['key']}');
}
// Stateful signature (args + state)
(Map args, WDynamicState state) {
final username = state.get('username');
print('User $username clicked ${args['button']}');
}
```
**Complete example:**
```dart
WDynamic(
json: {
'type': 'WButton',
'props': {
'className': 'bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded',
'onTap': {'action': 'submitForm', 'args': {'formId': 'login'}},
},
'children': [
{'type': 'WText', 'props': {'text': 'Login'}}
],
},
actions: {
'submitForm': (Map args, WDynamicState state) {
final formId = args['formId'];
final email = state.get('email');
final password = state.get('password');
print('Submitting $formId: email=$email');
},
},
)
```
> [!NOTE]
> If an action name is not found in the `actions` map, WDynamic logs a warning and continues silently. This prevents crashes from misconfigured JSON.
## Form State Management
Widgets with an `id` prop automatically register their values in a shared state store. This eliminates manual state wiring for form fields.
**Auto-tracked widgets:**
- `WInput` - Tracks text value
- `WCheckbox` - Tracks boolean checked state
- `WSelect` - Tracks selected value
- `WDatePicker` - Tracks DateTime value
**Accessing state from actions:**
```dart
WDynamic(
json: {
'type': 'WDiv',
'props': {'className': 'flex flex-col gap-4'},
'children': [
{
'type': 'WInput',
'props': {
'id': 'username', // Auto-tracked
'placeholder': 'Enter name...',
},
},
{
'type': 'WButton',
'props': {
'onTap': {'action': 'greet'},
},
'children': [
{'type': 'WText', 'props': {'text': 'Greet'}}
],
},
],
},
actions: {
'greet': (args, state) {
final name = state.get('username') ?? 'Guest';
print('Hello, $name!');
},
},
)
```
**External state access with WDynamicController:**
The controller API allows you to read/write form values from outside the WDynamic widget:
```dart
final controller = WDynamicController();
// Pre-fill values
controller.setValue('email', 'user@example.com');
controller.setValue('newsletter', true);
// Pass to WDynamic
WDynamic(
json: myFormJson,
controller: controller,
actions: myActions,
)
// Read values externally
final email = controller.getValue('email');
final allValues = controller.getAll(); // {'email': '...', 'newsletter': true}
// Listen for changes
final dispose = controller.addListener('email', (value) {
print('Email changed: $value');
});
// Clean up
controller.dispose();
```
**State API reference:**
| Method | Description |
|:-------|:------------|
| `getValue(id)` | Get a single value by id |
| `setValue(id, value)` | Set a value (triggers listeners) |
| `getAll()` | Get all values as a map |
| `reset()` | Clear all values |
| `addListener(id, callback)` | Listen for changes to a specific id |
## Security & Whitelisting
Because JSON can come from untrusted sources (user input, third-party APIs), WDynamic uses a **whitelist-only** security model. Only explicitly allowed widget types can render.
**Default allowed widgets:**
Wind widgets:
- `WDiv`, `WText`, `WButton`, `WImage`, `WIcon`, `WAnchor`, `WInput`, `WCheckbox`, `WSvg`, `WSelect`, `WPopover`, `WDatePicker`, `WSpacer`
Flutter core widgets:
- `Column`, `Row`, `Center`, `SizedBox`, `Expanded`, `Container`, `Wrap`, `Stack`, `Positioned`, `Padding`, `Align`, `Opacity`, `AspectRatio`, `FittedBox`, `ClipRRect`, `Spacer`
**Restricting widgets:**
Use the `denyWidgets` parameter to remove specific types from the whitelist:
```dart
WDynamic(
json: untrustedJson,
denyWidgets: {'WButton', 'Stack'}, // Disable buttons and stacks
)
```
Any attempt to render a denied widget will trigger the `onUnknownWidget` callback or render an empty container.
**Why whitelist-only?**
Arbitrary widget instantiation could expose dangerous APIs (file system access, network calls, etc.). The whitelist ensures that only safe, layout-focused widgets can be dynamically rendered.
## Custom Widget Builders
Extend WDynamic with your own widget types using the `builders` parameter:
```dart
WDynamic(
json: {
'type': 'ProfileCard',
'props': {
'name': 'Alice',
'role': 'Engineer',
'avatar': 'https://example.com/avatar.png',
},
},
builders: {
'ProfileCard': (Map props, List children) {
return WDiv(
className: 'flex items-center gap-4 p-4 bg-white rounded-lg shadow',
children: [
WImage(props['avatar'], className: 'w-12 h-12 rounded-full'),
WDiv(
className: 'flex flex-col',
children: [
WText(props['name'] ?? '', className: 'font-bold'),
WText(props['role'] ?? '', className: 'text-sm text-gray-600'),
],
),
],
);
},
},
)
```
**Builder signature:**
```dart
typedef WWidgetBuilder = Widget Function(
Map props,
List children,
);
```
Custom builders receive the parsed `props` map and any resolved `children` widgets. You can extract typed values, apply conditional logic, or compose complex layouts.
> [!WARNING]
> Custom builders bypass the security whitelist. Only register builders for widgets you trust. Never pass user-provided builder functions.
## Custom Icons
For `WIcon` widgets in your JSON, you can provide custom icon mappings without building a full custom widget builder. Use the `customIcons` prop to map string names to `IconData`:
```dart
WDynamic(
json: {
'type': 'WIcon',
'props': {'icon': 'myCustomIcon'},
},
customIcons: {
'myCustomIcon': Icons.star,
'myOtherIcon': Icons.favorite,
},
)
```
This is a lightweight alternative to custom builders when you only need to extend the icon library. See [WDynamic — Custom Icons](../widgets/w-dynamic.md#custom-icons) for complete configuration details.
## Error Handling
WDynamic provides two callbacks for handling rendering failures:
**onUnknownWidget** - Called when a widget type is not in the whitelist:
```dart
WDynamic(
json: {'type': 'UnsafeWidget', 'props': {}},
onUnknownWidget: (String type, Map props) {
return WText(
'Widget "$type" not allowed',
className: 'text-red-500 p-2 bg-red-50 rounded',
);
},
)
```
**onError** - Called when a widget build throws an exception:
```dart
WDynamic(
json: myJson,
onError: (String type, Object error) {
return WDiv(
className: 'p-4 bg-red-100 border border-red-300 rounded',
child: WText('Error rendering $type: $error'),
);
},
)
```
Both callbacks are optional. If not provided, WDynamic renders an empty `SizedBox.shrink()` for failures.
**Maximum depth protection:**
To prevent infinite recursion or stack overflows from deeply nested JSON, WDynamic enforces a maximum depth limit (default: 50 levels):
```dart
WDynamic(
json: deeplyNestedJson,
maxDepth: 100, // Increase if needed
)
```
## Related Documentation
- [WDynamic Widget](../widgets/w-dynamic.md) - Full API reference
- [State Management](./state-management.md) - Interactive states and WAnchor
- [Utility-First Fundamentals](./utility-first.md) - Wind className syntax
- [WButton](../widgets/w-button.md) - Action-driven button widget
- [WInput](../widgets/w-input.md) - Form input with state tracking
---
# Debugging
# Debugging
- [Introduction](#introduction)
- [Enabling Debug Mode](#enabling-debug-mode)
- [Understanding the Output](#understanding-the-output)
- [Composition Tree](#composition-tree)
- [Final Styles](#final-styles)
- [Build Time](#build-time)
- [How WindLogger Works](#how-windlogger-works)
- [Quick Reference](#quick-reference)
- [Common Scenarios](#common-scenarios)
- [External Tooling Integration](#external-tooling-integration)
## Introduction
Wind includes a specialized debugging system designed to peel back the abstraction of the utility-first engine. Since Wind translates a single `className` string into a complex hierarchy of Flutter widgets, the debugging tools allow you to verify the exact widget composition, resolved styles, and build performance.
The system is built directly into the core engine, requiring no external configuration or devtools extensions to inspect the "parsing → styling → building" pipeline.
```dart
WDiv(
className: 'debug flex p-4 bg-blue-500 rounded-lg',
child: WText('Debugging active'),
)
```
## Enabling Debug Mode
To enable debugging for a specific widget, add the `debug` utility class to its `className` string. This approach allows for targeted inspection without flooding the console with logs from unrelated widgets.
```dart
// Enabling debug for a button
WButton(
className: 'debug bg-red-500 hover:bg-red-600 px-4 py-2 rounded',
onTap: () => print('Action'),
child: WText('Delete'),
)
```
When the `WindParser` encounters the `debug` class, it triggers a detailed report to the console during the widget's build phase.
> [!NOTE]
> Debug mode is scoped to the specific widget instance. It does not affect parent or child widgets unless they also include the `debug` class.
## Understanding the Output
The debug report is divided into three primary sections that represent the lifecycle of a Wind widget.
### Composition Tree
The Composition Tree visualizes the "Atomic Widget" stack. Because Wind often wraps your content in multiple Flutter widgets (such as `Padding`, `DecoratedBox`, or `ConstrainedBox`) to satisfy the `className` requirements, this tree shows the final structure.
```text
--- [WIND DEBUG] Composition Tree: ---
Padding(
padding: EdgeInsets.all(16.0),
child:
DecoratedBox(
decoration: BoxDecoration(
color: Color(0xFF3B82F6),
borderRadius: BorderRadius.circular(8.0)
),
child: ...
)
)
```
### Final Styles
This section displays the fully resolved `WindStyle` object. This is the final state after all utility classes, responsive modifiers (e.g., `md:`), and state variants (e.g., `hover:`) have been merged.
```text
--- [WIND DEBUG] Final Styles: WindStyle(
padding: EdgeInsets.all(16.0),
backgroundColor: Color(0xFF3B82F6),
borderRadius: BorderRadius.circular(8.0),
isFlex: true,
) ---
```
### Build Time
Wind measures the total time spent in the style resolution and widget building process. Metrics are provided in microseconds (µs) to ensure high-precision performance monitoring.
```text
--- [WIND DEBUG] Build Time: 142µs ---
```
Significant spikes in build time (typically >1ms) may indicate excessive class complexity or inefficient custom state resolutions.
## How WindLogger Works
Internally, Wind leverages a `WindLogger` service. When the `WindParser` resolves a `className`, it checks for the presence of the `debug` flag in the resulting style object.
If active, the parser passes the resolved properties and the generated widget hierarchy to the logger. The logger then formats this data into a human-readable "pseudo-Dart" format. The timing is handled by a `Stopwatch` that starts when the parsing begins and stops when the widget tree is returned.
## Quick Reference
| Field | Purpose | Output Format |
|:------|:--------|:--------------|
| `debug` | Utility class | N/A |
| `Composition Tree` | Widget hierarchy | Pseudo-Dart code |
| `Final Styles` | Style verification | `WindStyle` properties |
| `Build Time` | Performance audit | Microseconds (µs) |
## Common Scenarios
Let's look at common debugging workflows using the `debug` class:
- **Inspecting Responsive Styles**: Resize the viewport while `debug` is active. The logger will re-trigger and show the new `Final Styles` for the active breakpoint.
- **Verifying State Variants**: Hover over or focus a widget with state-based classes (like `hover:bg-blue-600`). The log will update to show the merged style.
- **Analyzing Layout Layers**: Use the `Composition Tree` to determine which utility class is responsible for a specific Flutter wrapper, helping identify unexpected padding or alignment issues.
## External Tooling Integration
Beyond the `debug` className for in-console inspection, Wind exposes a contracts-based bridge so external debug tools (E2E drivers, runtime inspectors, IDE plugins) can read live Wind state per widget. The bridge ships in the production dependency `fluttersdk_wind_diagnostics_contracts` and is installed via a single facade call.
### When you need this
You need to call `Wind.installDebugResolver()` when one of these is true:
- You are using `fluttersdk_dusk` to drive E2E tests against your app. Dusk reads Wind state (the active className, breakpoint, brightness, platform, dynamic states, resolved background color, resolved text color) via the contracts package during snapshot capture.
- You are writing an inspector or IDE tool that needs the same per-widget Wind state at runtime.
If neither applies, you can skip this step. Wind itself does not depend on the resolver being installed; widgets render the same with or without it.
### Wiring it up
Call `Wind.installDebugResolver()` once during app startup, inside a `kDebugMode` guard. The call is idempotent (safe to call multiple times) and is automatically a no-op in release builds, so the bridge is tree-shaken out of production binaries.
```dart
import 'package:flutter/foundation.dart';
import 'package:fluttersdk_wind/fluttersdk_wind.dart';
void main() {
if (kDebugMode) {
Wind.installDebugResolver();
}
runApp(const MyApp());
}
```
After the call, any consumer of `WindDebugRegistry.current` (from the contracts package) can call `resolver.resolve(element)` on a Wind widget's `Element` to receive a map of debug fields: `className`, `breakpoint`, `brightness`, `platform`, `states`, `bgColor`, `textColor`. Tools read this without depending on Wind directly.
### What the contract emits
`WindDebugResolverImpl.resolve(element)` returns at most six fields per widget element:
| Field | Type | Notes |
|:------|:-----|:------|
| `className` | `String` | The active className resolved through breakpoints, dark mode, and state prefixes |
| `breakpoint` | `String` | The currently-active named breakpoint (`sm` / `md` / `lg` / `xl` / `2xl` / `base`) |
| `brightness` | `String` | `'light'` or `'dark'` |
| `platform` | `String` | `'web'` / `'android'` / `'ios'` / etc. |
| `states` | `List` | Active dynamic states (`hover`, `focus`, `active`, custom keys) |
| `bgColor` | `String?` | Resolved background color hex (8 digits including alpha) when set by className or inline prop |
| `textColor` | `String?` | Resolved foreground color hex when set |
Fields whose value is null are omitted; the map only carries what the widget actually resolved.
### Release-build safety
`Wind.installDebugResolver()` is a no-op in release builds. The `kDebugMode` guard in user code further ensures dart2js / dart2native tree-shakes the entire branch on every platform. There is no production cost from leaving the call in place.
---
# Display
# Display
Utilities for controlling the layout mode of an element.
- [Basic Usage](#basic-usage)
- [Block](#block)
- [Flex](#flex)
- [Grid](#grid)
- [Wrap](#wrap)
- [Hidden](#hidden)
- [Quick Reference](#quick-reference)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [State Variants](#state-variants)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Flex container
WDiv(className: 'flex gap-4', children: [...])
// Grid container
WDiv(className: 'grid grid-cols-3', children: [...])
// Hidden element
WDiv(className: 'hidden', child: ...)
```
## Basic Usage
### Block
Use `block` to create a standard block-level element. In Wind, this is the default behavior of `WDiv` (rendering a `Container` or `SizedBox`).
```dart
WDiv(
className: 'block p-4 bg-white',
child: WText('This is a block element'),
)
```
### Flex
Use `flex` to create a flex container. This enables Flexbox layout properties (like `flex-row`, `items-center`, `justify-between`).
> [!NOTE]
> `flex` creates a `Row` or `Column` depending on `flex-row` (default) or `flex-col`.
```dart
WDiv(
className: 'flex items-center space-x-4',
children: [
WDiv(className: 'w-10 h-10 bg-blue-500'),
WText('Flex item'),
],
)
```
### Grid
Use `grid` to create a grid container. This enables Grid layout properties (like `grid-cols-3`, `gap-4`).
```dart
WDiv(
className: 'grid grid-cols-3 gap-4',
children: [
WDiv(className: 'h-20 bg-blue-100'),
WDiv(className: 'h-20 bg-blue-200'),
WDiv(className: 'h-20 bg-blue-300'),
],
)
```
### Wrap
Use `wrap` to create a wrapping container. This renders a Flutter `Wrap` widget.
> [!WARNING]
> Do **not** use `flex flex-wrap`. In Flutter, `Row`/`Column` cannot wrap. Always use the `wrap` display utility instead.
```dart
WDiv(
className: 'wrap gap-2',
children: [
WChip('React'),
WChip('Flutter'),
WChip('Vue'),
],
)
```
### Hidden
Use `hidden` to remove an element from the visual tree. This renders a `SizedBox.shrink()` (effectively 0x0 size).
```dart
WDiv(
className: 'hidden',
child: WText('This will not appear'),
)
```
## Quick Reference
| Class | Description |
| :--- | :--- |
| `block` | Standard box model layout (Default). |
| `flex` | Creates a Flex container (Row/Column). |
| `grid` | Creates a Grid container. |
| `wrap` | Creates a Wrap container. |
| `hidden` | Removes the element from the layout. |
## Responsive Design
Prefix any display utility with a breakpoint variant like `md:` to apply it only at specific screen sizes.
```dart
// Hidden on mobile, Flex on medium screens and up
WDiv(
className: 'hidden md:flex gap-4',
children: [...],
)
// Block on mobile, Hidden on large screens
WDiv(
className: 'block lg:hidden',
child: ...,
)
```
## Dark Mode
Prefix any display utility with `dark:` to apply it only when dark mode is active.
```dart
// Visible only in dark mode
WDiv(
className: 'hidden dark:block',
child: WText('Dark Mode Active'),
)
```
## State Variants
Prefix utilities with state variants like `hover:`, `focus:`, or `active:` to conditionally apply display modes.
```dart
// Show element on hover
WDiv(
className: 'hidden group-hover:block',
child: ...,
)
```
## Arbitrary Values
The display utility does not support arbitrary values. You must use one of the predefined keywords (`block`, `flex`, `grid`, `wrap`, `hidden`).
## Customizing Theme
Display utilities are structural and cannot be customized via `WindThemeData`. They map directly to Flutter widgets (`Row`, `Column`, `Wrap`, `GridView`).
## Related Documentation
- [Flexbox](./flexbox.md) - Layout direction, alignment, and sizing for flex containers.
- [Grid](./grid.md) - Column definitions and gaps for grid containers.
- [Visibility](./visibility.md) - Control visibility without changing layout (opacity).
---
# Flexbox & Layout
# Flexbox & Layout
Utilities for controlling flex containers, direction, alignment, wrapping, and spacing.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Flex Direction](#flex-direction)
- [Reversing Direction](#reversing-direction)
- [Child Order](#child-order)
- [Wrapping](#wrapping)
- [Justify Content](#justify-content)
- [Align Items](#align-items)
- [Align Content](#align-content)
- [Align Self](#align-self)
- [Flex, Grow & Shrink](#flex-grow--shrink)
- [Gap & Spacing](#gap--spacing)
- [Responsive Design](#responsive-design)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Flex Children Inside a Scrollable Axis](#flex-and-overflow)
- [Related Documentation](#related-documentation)
```dart
// Basic flex row
WDiv(className: 'flex gap-4 items-center')
// Vertical column
WDiv(className: 'flex flex-col gap-2')
```
## Basic Usage
Flexbox in Wind mirrors CSS Flexbox behavior but maps to Flutter's `Row` and `Column` widgets. Use `flex` to initialize a flex container, then apply utilities to control direction, alignment, and spacing.
```dart
WDiv(
className: 'flex flex-col md:flex-row justify-between items-center gap-4 p-6 bg-white',
children: [
WText('Logo'),
WDiv(
className: 'flex gap-2',
children: [
WText('Home'),
WText('About'),
],
),
],
)
```
## Quick Reference
| Class | CSS Equivalent | Flutter Equivalent |
|:------|:---------------|:-------------------|
| `flex` | `display: flex` | `Row` / `Column` |
| `wrap` | `flex-wrap: wrap` | `Wrap` |
| `flex-row` | `flex-direction: row` | `Row()` |
| `flex-col` | `flex-direction: column` | `Column()` |
| `flex-row-reverse` | `flex-direction: row-reverse` | `Row(textDirection: mirrored)` |
| `flex-col-reverse` | `flex-direction: column-reverse` | `Column(verticalDirection: up)` |
| `order-{n}` | `order: {n}` | Stable-sort children before layout |
| `justify-{alignment}` | `justify-content: ...` | `MainAxisAlignment` |
| `items-{alignment}` | `align-items: ...` | `CrossAxisAlignment` |
| `gap-{n}` | `gap: {n}` | `SizedBox` (spacer) |
| `flex-1` | `flex: 1` | `Expanded()` |
| `shrink-0` | `flex-shrink: 0` | No wrapper — preserves intrinsic size |
## Flex Direction
Control the axis of your layout.
```dart
// Row (Horizontal) - Default for 'flex'
WDiv(className: 'flex flex-row')
// Column (Vertical)
WDiv(className: 'flex flex-col')
```
## Reversing Direction
Use `flex-row-reverse` or `flex-col-reverse` to reverse the main-axis direction. Rather than reversing the children list, Wind flips the axis itself (via `Row.textDirection` / `Column.verticalDirection`), so alignment tokens mirror correctly: `justify-start` anchors to what is now the visual end, matching CSS's `flex-direction: *-reverse`. Cross-axis alignment is unaffected.
```dart
WDiv(
className: 'flex flex-row-reverse gap-2',
children: [
WText('First in code'),
WText('Last in code'),
],
)
// Renders as: [Last in code] [First in code]
```
Responsive prefixes work as expected:
```dart
// Column on mobile, row-reverse on md+
WDiv(className: 'flex flex-col md:flex-row-reverse gap-4')
```
## Child Order
Use `order-*` on individual flex children to override their paint order without changing the source order. The parent flex container stable-sorts children by their resolved `order` value before laying them out; children without `order-*` default to `0`.
| Class | Behavior |
|:------|:---------|
| `order-0` .. `order-12` | Explicit index (integers only) |
| `order-first` | Placed before everything else (sentinel `-9999`) |
| `order-last` | Placed after everything else (sentinel `9999`) |
| `order-none` | Resets to `0` |
| `order-[n]` | Arbitrary integer (supports negative, e.g. `order-[-3]`) |
```dart
WDiv(
className: 'flex',
children: [
WDiv(className: 'order-3', child: WText('A')),
WDiv(className: 'order-1', child: WText('B')),
WDiv(className: 'order-2', child: WText('C')),
],
)
// Visual order: B, C, A
```
Responsive reordering is a common use case — swap the layout per breakpoint without duplicating children:
```dart
WDiv(
className: 'flex flex-col md:flex-row gap-4',
children: [
WDiv(className: 'order-2 md:order-1', child: WText('Sidebar')),
WDiv(className: 'order-1 md:order-2', child: WText('Main content')),
],
)
```
When combined with `flex-*-reverse`, children are sorted by `order-*` first; the container then flips the main-axis direction so `justify-*` still applies against the (now reversed) axis.
## Wrapping
Use `wrap` to create a wrapping layout (Flutter's `Wrap` widget).
> [!WARNING]
> Flutter's `Row` and `Column` do **not** wrap. You must use the `wrap` class instead of `flex` if you want wrapping behavior.
```dart
// Items wrap to the next line when they run out of space
WDiv(
className: 'wrap gap-2',
children: [
WDiv(className: 'px-2 py-1 bg-gray-200 rounded', child: WText('Tag 1')),
WDiv(className: 'px-2 py-1 bg-gray-200 rounded', child: WText('Tag 2')),
// ...
],
)
```
## Justify Content
Controls how children are distributed along the **main axis** (Horizontal for `row`, Vertical for `col`).
| Class | Flutter `MainAxisAlignment` |
|:------|:----------------------------|
| `justify-start` | `start` |
| `justify-end` | `end` |
| `justify-center` | `center` |
| `justify-between` | `spaceBetween` |
| `justify-around` | `spaceAround` |
| `justify-evenly` | `spaceEvenly` |
```dart
WDiv(
className: 'flex justify-between',
children: [
WDiv(className: 'w-10 h-10 bg-red-500'),
WDiv(className: 'w-10 h-10 bg-blue-500'),
],
)
```
## Align Items
Controls how children are distributed along the **cross axis** (Vertical for `row`, Horizontal for `col`).
| Class | Flutter `CrossAxisAlignment` |
|:------|:-----------------------------|
| `items-start` | `start` |
| `items-end` | `end` |
| `items-center` | `center` |
| `items-baseline` | `baseline` |
| `items-stretch` | `stretch` |
```dart
// Vertically center items in a row
WDiv(className: 'flex items-center h-20')
```
## Align Content
Only applicable when using `wrap`. Controls how lines of wrapped content are aligned.
| Class | Flutter `WrapAlignment` |
|:------|:------------------------|
| `align-content-start` | `start` |
| `align-content-end` | `end` |
| `align-content-center` | `center` |
| `align-content-between` | `spaceBetween` |
| `align-content-around` | `spaceAround` |
| `align-content-evenly` | `spaceEvenly` |
| `align-content-stretch` | `start` (Flutter limitation) |
## Align Self
Control alignment of an individual flex item, overriding the container's `items-*` setting.
| Class | Flutter `Alignment` |
|:------|:--------------------|
| `align-self-start` | `topCenter` |
| `align-self-end` | `bottomCenter` |
| `align-self-center` | `center` |
| `align-self-stretch` | `center` |
| `align-self-auto` | `center` |
```dart
WDiv(
className: 'flex items-start h-20',
children: [
WDiv(className: '...'),
// This item centers itself
WDiv(className: 'align-self-center ...'),
],
)
```
## Flex, Grow & Shrink
Control how individual children resize to fill available space.
| Class | Description |
|:------|:------------|
| `flex-1` | Allow child to grow and fill available space (`Expanded`). |
| `flex-grow` | Alias for `flex-1`. |
| `flex-{n}` | Specific flex factor (e.g., `flex-2`). |
| `shrink` | Allow child to shrink if needed (`FlexFit.loose`). |
| `shrink-0` | Preserve intrinsic size — no Flexible wrapper, child keeps its natural dimensions. |
| `flex-none` | Do not grow or shrink. |
```dart
WDiv(
className: 'flex',
children: [
// Sidebar: Fixed width, won't shrink
WDiv(className: 'w-16 shrink-0 bg-gray-200'),
// Content: Fills remaining space
WDiv(
className: 'flex-1 bg-white p-4',
child: WText('Main Content'),
),
],
)
```
## Gap & Spacing
Wind's `gap` utilities add space between flex or grid items without using margin on the children themselves.
| Class | Value (Default) | Description |
|:------|:----------------|:------------|
| `gap-0` | 0px | No gap |
| `gap-1` | 4px | Small gap |
| `gap-2` | 8px | Medium gap |
| `gap-4` | 16px | Large gap |
| `gap-x-{n}` | - | Horizontal gap only |
| `gap-y-{n}` | - | Vertical gap only |
> [!NOTE]
> `space-x-{n}` and `space-y-{n}` are supported as aliases for `gap`, but `gap` is preferred for consistency with Grid.
```dart
// 16px gap horizontally and vertically
WDiv(className: 'flex gap-4')
// 8px horizontal, 16px vertical
WDiv(className: 'flex flex-col gap-x-2 gap-y-4')
```
## Responsive Design
Change layout direction or spacing based on screen size. This is extremely powerful for mobile-first designs.
```dart
// Column on mobile, Row on tablet+
WDiv(className: 'flex flex-col md:flex-row gap-4 md:gap-8')
```
## Arbitrary Values
Need a specific gap or flex value not in the theme? Use bracket notation.
```dart
// Specific 23px gap
WDiv(className: 'flex gap-[23px]')
// Specific pixel gap
WDiv(className: 'flex gap-[1.5rem]')
```
## Customizing Theme
To change the default spacing scale used by `gap`, modify `baseSpacingUnit` or the `containers` map in `WindThemeData`.
```dart
WindThemeData(
baseSpacingUnit: 8.0, // Now gap-1 = 8px, gap-2 = 16px
)
```
## Flex Children Inside a Scrollable Axis
When a flex container's own main axis is scrollable (`flex-row` with `overflow-x-auto|scroll`, or `flex-col` with `overflow-y-auto|scroll`), Flutter's `Expanded`/`Flexible` cannot resolve — the incoming constraint is unbounded. Wind detects this at resolution time and skips the `Expanded`/`Flexible` wrap for direct flex children in that pass, so `flex-N` becomes a no-op while the axis is scrollable.
This enables the common responsive pattern of "scroll on narrow, distribute on wide":
```dart
WDiv(
className: 'flex flex-row gap-1 overflow-x-auto sm:overflow-visible',
children: [
for (final tab in tabs)
WDiv(className: 'flex-1', child: _tabBtn(tab)),
],
)
```
At `base` the row scrolls horizontally and children keep their intrinsic width. At `sm+` the overflow is removed and `flex-1` distributes width equally. Nested non-scrolling flex subtrees under a scrolling ancestor behave normally.
## Related Documentation
- [Grid Layout](./grid.md)
- [Display Modes](./display.md)
- [Sizing](../sizing/width.md)
---
# Grid Template Columns
# Grid Template Columns
Utilities for specifying the columns in a grid layout.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Responsive](#responsive)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Related Documentation](#related-documentation)
```dart
// Basic grid with 3 columns
WDiv(
className: 'grid grid-cols-3 gap-4',
children: [
WDiv(className: 'bg-blue-500 h-12'),
WDiv(className: 'bg-blue-500 h-12'),
WDiv(className: 'bg-blue-500 h-12'),
],
)
```
## Basic Usage
Use `grid-cols-{n}` to create a grid with `n` equally sized columns.
This utility must be used alongside the `grid` class (or `inline-grid` if supported) to activate the grid layout model.
```dart
WDiv(
className: 'grid grid-cols-4 gap-4',
children: [
// ... 4 items per row
],
)
```
## Quick Reference
| Class | Properties |
|:--- |:--- |
| `grid-cols-1` | `crossAxisCount: 1` |
| `grid-cols-2` | `crossAxisCount: 2` |
| `grid-cols-3` | `crossAxisCount: 3` |
| `grid-cols-4` | `crossAxisCount: 4` |
| `grid-cols-5` | `crossAxisCount: 5` |
| `grid-cols-6` | `crossAxisCount: 6` |
| `grid-cols-12` | `crossAxisCount: 12` |
> [!NOTE]
> Wind's parser supports **any integer** value for `grid-cols-{n}`, not just the standard Tailwind scale (1-12).
## Responsive
Prefix grid utilities with breakpoint variants like `md:` or `lg:` to change the column count at different screen sizes.
```dart
WDiv(
className: 'grid grid-cols-1 md:grid-cols-3 lg:grid-cols-6 gap-4',
children: [
// 1 col on mobile, 3 on tablet, 6 on desktop
],
)
```
## Dark Mode
While column counts rarely change based on theme, you can use `dark:` if needed.
```dart
WDiv(
className: 'grid grid-cols-2 dark:grid-cols-4',
children: [...],
)
```
## Arbitrary Values
Wind supports any integer for grid columns directly in the utility class. You do not need square brackets `[]` for this property.
```dart
// Create a grid with exactly 16 columns
WDiv(
className: 'grid grid-cols-16 gap-1',
children: [...],
)
```
## Related Documentation
- [Flexbox & Gap](./flexbox.md) - For `gap-{n}`, `gap-x-{n}`, and `gap-y-{n}` utilities.
- [Display](./display.md) - For the `grid` class.
---
# Positioning
# Positioning
Utilities for controlling how elements are positioned in the layout using `relative` and `absolute` placement.
- [Position Types](#position-types)
- [Offset Utilities](#offset-utilities)
- [Inset Shortcuts](#inset-shortcuts)
- [Negative Offsets](#negative-offsets)
- [Arbitrary Values](#arbitrary-values)
- [Common Patterns](#common-patterns)
- [Combining with Flex](#combining-with-flex)
- [Future Work](#future-work)
- [Related Documentation](#related-documentation)
```dart
// Badge overlay — red dot on an avatar
WDiv(
className: 'relative',
children: [
WDiv(className: 'w-12 h-12 rounded-full bg-gray-300'),
WDiv(className: 'absolute top-0 right-0 w-3 h-3 rounded-full bg-red-500'),
],
)
```
## Position Types
Wind maps CSS `position` values to Flutter's `Stack` widget. A `relative` parent establishes a stacking context; `absolute` children are placed inside it using offset utilities.
| Wind className | CSS Equivalent | Flutter Widget |
|:---------------|:---------------|:---------------|
| `relative` | `position: relative` | `Stack` (parent) |
| `absolute` | `position: absolute` | `Positioned` (child inside `Stack`) |
> [!NOTE]
> An `absolute` child must live inside a `relative` parent. Wind will wrap the `relative` container in a `Stack` and each `absolute` child in a `Positioned` widget automatically.
```dart
WDiv(
className: 'relative w-32 h-32 bg-gray-100',
children: [
WDiv(className: 'absolute top-2 left-2 w-8 h-8 bg-blue-500'),
],
)
```
## Offset Utilities
Control the position of `absolute` children using directional offset classes. Values follow the spacing scale (`spacing * n`).
| Class | CSS Equivalent | Description |
|:------|:---------------|:------------|
| `top-{n}` | `top: {n}` | Distance from the top edge |
| `right-{n}` | `right: {n}` | Distance from the right edge |
| `bottom-{n}` | `bottom: {n}` | Distance from the bottom edge |
| `left-{n}` | `left: {n}` | Distance from the left edge |
Default spacing scale (base unit = 4px):
| Class | Value |
|:------|:------|
| `top-0` | 0px |
| `top-1` | 4px |
| `top-2` | 8px |
| `top-4` | 16px |
| `top-6` | 24px |
| `top-8` | 32px |
The same scale applies to `right-*`, `bottom-*`, and `left-*`.
```dart
WDiv(
className: 'relative h-48 bg-white border border-gray-200 rounded-lg',
children: [
WDiv(
className: 'absolute bottom-4 right-4 px-3 py-2 bg-blue-600 rounded',
child: WText('Action', className: 'text-sm text-white'),
),
],
)
```
## Inset Shortcuts
Apply offsets to multiple sides at once with `inset-*` shorthand classes.
| Class | Sides Affected | Description |
|:------|:---------------|:------------|
| `inset-{n}` | top, right, bottom, left | All four sides |
| `inset-x-{n}` | left, right | Horizontal sides only |
| `inset-y-{n}` | top, bottom | Vertical sides only |
| `inset-0` | top, right, bottom, left | Full stretch (fills parent) |
```dart
// Full overlay — covers the entire relative parent
WDiv(
className: 'relative w-full h-48',
children: [
WImage(src: 'assets/photo.jpg', className: 'w-full h-full'),
// Semi-transparent overlay that fills the image
WDiv(
className: 'absolute inset-0 bg-black opacity-40',
),
WText(
'Caption',
className: 'absolute bottom-4 left-4 text-white font-semibold',
),
],
)
```
```dart
// Horizontal inset — leaves top and bottom unconstrained
WDiv(
className: 'absolute inset-x-4 bottom-4 bg-white rounded p-3',
child: WText('Bottom bar'),
)
```
## Negative Offsets
Prefix any offset class with `-` to pull an element outside its parent's boundary.
| Class | Value | Description |
|:------|:------|:------------|
| `-top-{n}` | negative top | Pulls element above the parent |
| `-right-{n}` | negative right | Pulls element to the right of the parent |
| `-bottom-{n}` | negative bottom | Pulls element below the parent |
| `-left-{n}` | negative left | Pulls element to the left of the parent |
| `-inset-{n}` | negative all sides | Expands element beyond all four parent edges |
```dart
// Notification badge — overlaps the top-right corner of an icon button
WDiv(
className: 'relative',
children: [
WDiv(className: 'w-10 h-10 rounded bg-gray-200 flex items-center justify-center'),
WDiv(
className: 'absolute -top-1 -right-1 w-4 h-4 rounded-full bg-red-500 flex items-center justify-center',
child: WText('3', className: 'text-[10px] text-white font-bold'),
),
],
)
```
## Arbitrary Values
Use bracket notation when you need an exact pixel value not in the theme scale. Only `px` values are supported — percentage (`%`) offsets are not supported because Flutter's `Positioned` widget uses logical pixels, not relative units.
```dart
// Exact pixel value
WDiv(className: 'absolute top-[24px] left-[12px]')
// Mixed — precise multi-side offset
WDiv(className: 'absolute top-[12px] right-[8px] bottom-[12px] left-[8px]')
```
## Common Patterns
### Badge Overlay
A notification dot positioned on top of an avatar or icon.
```dart
WDiv(
className: 'relative w-12 h-12',
children: [
WDiv(className: 'w-12 h-12 rounded-full bg-indigo-500'),
WDiv(
className: 'absolute -top-1 -right-1 w-5 h-5 rounded-full bg-red-500 border-2 border-white flex items-center justify-center',
child: WText('2', className: 'text-[9px] text-white font-bold'),
),
],
)
```
### Floating Action Button (FAB)
An action button pinned to the bottom-right corner of a scrollable view.
```dart
WDiv(
className: 'relative flex-1',
children: [
WDiv(
className: 'w-full h-full overflow-y-auto p-4',
child: WText('Scrollable content...'),
),
WDiv(
className: 'absolute bottom-6 right-6 w-14 h-14 rounded-full bg-blue-600 flex items-center justify-center shadow-lg',
child: WIcon(Icons.add_outlined, className: 'text-white'),
),
],
)
```
### Full Overlay
A semi-transparent scrim that covers an entire card or image.
```dart
WDiv(
className: 'relative rounded-xl overflow-hidden',
children: [
WImage(src: 'assets/hero.jpg', className: 'w-full h-48 object-cover'),
WDiv(className: 'absolute inset-0 bg-gradient-to-t from-black/60 to-transparent'),
WDiv(
className: 'absolute bottom-0 left-0 right-0 p-4',
child: WText('Hero Title', className: 'text-white text-lg font-bold'),
),
],
)
```
## Combining with Flex
`relative` and `absolute` compose naturally with flex layouts. The `relative` container itself can be a flex row or column — the `Stack` wraps around the flex widget, and `absolute` children are layered on top.
```dart
// Navigation bar with an absolute badge on the icon
WDiv(
className: 'flex flex-row items-center justify-between px-4 py-3 bg-white border-b border-gray-200',
children: [
WText('Inbox', className: 'text-base font-semibold text-gray-900'),
WDiv(
className: 'relative',
children: [
WIcon(Icons.notifications_outlined, className: 'text-gray-700'),
WDiv(
className: 'absolute -top-1 -right-1 w-4 h-4 rounded-full bg-red-500',
),
],
),
],
)
```
> [!NOTE]
> When you add `absolute` children to a flex item, Wind promotes that item to a `Stack`. The flex layout of the parent is preserved.
## Future Work
The following position types are tracked but not yet implemented in v1:
| Class | Status |
|:------|:-------|
| `fixed` | Deferred — maps to Flutter's `Overlay`/`Stack` at the root level |
| `sticky` | Deferred — requires custom `SliverPersistentHeader` integration |
Until these land, use `Overlay` directly or Flutter's `Stack` at the `Scaffold` body level for fixed positioning.
## Related Documentation
- [Flexbox & Layout](./flexbox.md)
- [Grid Layout](./grid.md)
- [Sizing](../sizing/width.md)
- [Spacing](../spacing/padding.md)
---
# Spacing
# Spacing
Utilities for controlling the padding and margin of elements.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Adds 16px padding inside, 16px margin outside
WDiv(className: 'p-4 m-4 bg-white')
```
## Basic Usage
Control the spacing of an element using the `p-{size}` (padding) and `m-{size}` (margin) utilities.
### Padding
Padding adds space **inside** the element's boundary.
```dart
// Add padding to all sides
WDiv(className: 'p-6 bg-blue-100', child: WText('Padding'))
```
### Margin
Margin adds space **outside** the element's boundary.
```dart
// Add margin to all sides
WDiv(className: 'm-4 bg-red-100', child: WText('Margin'))
```
## Quick Reference
The default spacing scale is based on a **4px** unit (`baseSpacingUnit: 4.0`).
| Class | Value (Default) | Description |
|:------|:----------------|:------------|
| `p-0` / `m-0` | 0px | No spacing |
| `p-1` / `m-1` | 4px | Tiny spacing |
| `p-2` / `m-2` | 8px | Small spacing |
| `p-4` / `m-4` | 16px | Normal spacing |
| `p-8` / `m-8` | 32px | Large spacing |
| `p-16` / `m-16` | 64px | Extra large spacing |
| `px-{n}` / `mx-{n}` | - | Horizontal (Left + Right) |
| `py-{n}` / `my-{n}` | - | Vertical (Top + Bottom) |
| `pt-{n}` / `mt-{n}` | - | Top only |
| `pr-{n}` / `mr-{n}` | - | Right only |
| `pb-{n}` / `mb-{n}` | - | Bottom only |
| `pl-{n}` / `ml-{n}` | - | Left only |
## Variants
### Directional Spacing
Target specific sides or axes using direction prefixes.
```dart
// Horizontal padding (Left + Right)
WDiv(className: 'px-4 bg-green-100')
// Vertical margin (Top + Bottom)
WDiv(className: 'my-6 bg-yellow-100')
// Top padding only
WDiv(className: 'pt-8 bg-purple-100')
```
### Horizontal Centering
Use `mx-auto` to center a container horizontally. This sets the left and right margins to align the element within its parent.
> [!NOTE]
> `mx-auto` only works when the element has a defined width less than its parent. Explicit `mx-{value}` classes (like `mx-0`) will override `mx-auto`.
```dart
WDiv(
className: 'mx-auto w-32 bg-blue-500',
child: WText('Centered'),
)
```
## Responsive Design
Apply different spacing at different breakpoints using the standard `sm:`, `md:`, `lg:`, `xl:`, and `2xl:` prefixes.
```dart
// p-4 on mobile, p-8 on medium screens, p-12 on large screens
WDiv(className: 'p-4 md:p-8 lg:p-12')
```
## Dark Mode
Use the `dark:` prefix to apply different spacing in dark mode.
```dart
WDiv(className: 'p-4 dark:p-6')
```
## Arbitrary Values
If the built-in scale doesn't meet your needs, use bracket notation to apply exact pixel values.
> [!WARNING]
> Wind parsers for padding and margin currently support **pixels** (`[10px]`, `[10]`) but do NOT support percentages (`[10%]`).
```dart
// Exact 13px padding
WDiv(className: 'p-[13px]')
// Exact 50px top margin
WDiv(className: 'mt-[50px]')
```
## Customizing Theme
To extend or override the default spacing scale, modify the `WindThemeData` in your app root.
The `baseSpacingUnit` controls the multiplier for all spacing utilities (`p-`, `m-`, `gap-`, `w-`, `h-`).
```dart
WindThemeData(
baseSpacingUnit: 8.0, // Sets 'p-1' to 8px, 'p-4' to 32px
)
```
## Related Documentation
- [Sizing](./sizing.md)
- [Flexbox](./flexbox.md) (Gap utilities)
- [Grid](./grid.md) (Gap utilities)
---
# Sizing
# Sizing
Utilities for setting the width and height of elements.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Width](#width)
- [Height](#height)
- [Min & Max Dimensions](#min--max-dimensions)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Fixed width and height
WDiv(className: 'w-64 h-32 bg-blue-500')
// Percentage based
WDiv(className: 'w-1/2 h-full bg-green-500')
// Viewport relative
WDiv(className: 'w-screen h-screen bg-gray-900')
```
## Basic Usage
Use `w-{size}` and `h-{size}` to set fixed dimensions based on the global spacing scale, or use fraction/percentage utilities like `w-1/2` or `w-full`.
```dart
WDiv(
className: 'w-full max-w-md mx-auto p-6 bg-white rounded-lg shadow-lg',
child: WText('Centered Content'),
)
```
## Quick Reference
### Fixed Sizes
Values follow the global spacing scale (1 unit = 4px).
| Class | Value | Description |
|:------|:------|:------------|
| `w-0` / `h-0` | 0px | Zero width/height |
| `w-px` / `h-px` | 1px | 1px width/height |
| `w-1` / `h-1` | 4px | Tiny |
| `w-4` / `h-4` | 16px | Small |
| `w-16` / `h-16` | 64px | Medium |
| `w-96` / `h-96` | 384px | Large |
### Percentages & Viewport
| Class | Value | Description |
|:------|:------|:------------|
| `w-full` / `h-full` | 100% | Full parent width/height |
| `w-screen` / `h-screen` | 100vw / 100vh | Full viewport width/height |
| `w-1/2` / `h-1/2` | 50% | Half parent size |
| `w-1/3` / `h-1/3` | 33.33% | Third parent size |
| `w-2/3` / `h-2/3` | 66.66% | Two-thirds parent size |
| `w-1/4` / `h-1/4` | 25% | Quarter parent size |
| `w-3/4` / `h-3/4` | 75% | Three-quarters parent size |
## Width
Set the width of an element using `w-{number}` or `w-{fraction}`.
```dart
// Fixed width (128px)
WDiv(className: 'w-32 bg-blue-200')
// Fractional width (50%)
WDiv(className: 'w-1/2 bg-blue-300')
// Full width (100%)
WDiv(className: 'w-full bg-blue-400')
```
## Height
Set the height of an element using `h-{number}`, `h-{fraction}`, or `h-screen`.
```dart
// Fixed height (64px)
WDiv(className: 'h-16 bg-red-200')
// Full screen height
WDiv(className: 'h-screen bg-red-300')
```
## Min & Max Dimensions
Constrain the size of elements using `min-w`, `max-w`, `min-h`, and `max-h` utilities.
### Max-Width Scale
Wind includes a comprehensive max-width scale, perfect for keeping text readable ("measure") or constraining layout width.
| Class | Value |
|:------|:------|
| `max-w-0` | 0px |
| `max-w-none` | No limit (Infinity) |
| `max-w-xs` | 320px |
| `max-w-sm` | 384px |
| `max-w-md` | 448px |
| `max-w-lg` | 512px |
| `max-w-xl` | 576px |
| `max-w-2xl` | 672px |
| `max-w-3xl` | 768px |
| `max-w-4xl` | 896px |
| `max-w-5xl` | 1024px |
| `max-w-6xl` | 1152px |
| `max-w-7xl` | 1280px |
| `max-w-full` | 100% |
| `max-w-screen` | 100vw |
| `max-w-prose` | 65ch (~1040px) |
```dart
// Constrain content width and center it
WDiv(
className: 'max-w-2xl mx-auto p-4',
child: WText('Readable content...'),
)
```
### Other Constraints
| Class | Value |
|:------|:------|
| `min-w-0` | 0px |
| `min-w-full` | 100% |
| `min-w-screen` | 100vw |
| `min-h-0` | 0px |
| `min-h-full` | 100% |
| `min-h-screen` | 100vh |
| `max-h-full` | 100% |
| `max-h-screen` | 100vh |
## Responsive Design
Apply different sizing utilities at different breakpoints using standard prefixes.
```dart
// Full width on mobile, 50% on tablet, 33% on desktop
WDiv(className: 'w-full md:w-1/2 lg:w-1/3')
```
## Dark Mode
While sizing rarely changes based on theme, you can use `dark:` modifiers if needed.
```dart
WDiv(className: 'w-64 dark:w-full')
```
## Arbitrary Values
Use square brackets to apply specific values that aren't in the theme scale.
```dart
// Specific pixels
WDiv(className: 'w-[350px] h-[50px]')
// Specific percentage
WDiv(className: 'w-[33%] h-[45%]')
```
## Customizing Theme
To extend the sizing scale, modify the `spacing` section of your `WindThemeData`. This affects width, height, padding, margin, and positioning.
```dart
WindThemeData(
spacing: {
'128': 512.0, // Adds w-128, h-128, p-128...
},
)
```
## Related Documentation
- [Padding & Margin](./spacing.md)
- [Flexbox & Grid](./flexbox.md)
- [Aspect Ratio](./aspect_ratio.md)
---
# Aspect Ratio
# Aspect Ratio
Utilities for controlling the aspect ratio of an element.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Basic examples showing the syntax
WDiv(className: 'aspect-square')
WDiv(className: 'aspect-video')
```
## Basic Usage
Use `aspect-{ratio}` to set the preferred aspect ratio of an element. This is particularly useful for images, video players, or ensuring consistent card sizing.
### Video (16:9)
Use `aspect-video` to force a 16:9 ratio, perfect for video content.
```dart
WDiv(
className: 'w-full aspect-video bg-black',
child: WText('Video Placeholder', className: 'text-white'),
)
```
### Square (1:1)
Use `aspect-square` to create a perfect square, regardless of width.
```dart
WDiv(
className: 'w-1/2 aspect-square bg-blue-500',
)
```
### Auto
Use `aspect-auto` to remove any applied aspect ratio constraints.
```dart
WDiv(className: 'aspect-auto')
```
## Quick Reference
| Class | Ratio | Description |
|:------|:------|:------------|
| `aspect-auto` | `null` | Default behavior (no ratio) |
| `aspect-square` | `1 / 1` | Square (1:1) |
| `aspect-video` | `16 / 9` | Standard video (16:9) |
## Variants
Wind supports standard state variants for aspect ratios.
```dart
// Change aspect ratio on hover
WDiv(className: 'aspect-square hover:aspect-video transition-all')
```
## Responsive Design
Apply different aspect ratios at different breakpoints using standard responsive prefixes.
```dart
// Square on mobile, Video on desktop
WDiv(className: 'aspect-square md:aspect-video')
```
## Dark Mode
While less common for layout structure, you can conditionally apply aspect ratios in dark mode.
```dart
WDiv(className: 'aspect-square dark:aspect-video')
```
## Arbitrary Values
Use the bracket syntax `aspect-[w/h]` to apply any specific aspect ratio that isn't included in the default theme.
```dart
// 4:3 ratio (Classic TV)
WDiv(className: 'aspect-[4/3]')
// 21:9 ratio (Ultrawide)
WDiv(className: 'aspect-[21/9]')
```
> [!NOTE]
> Arbitrary values must use the format `width/height` where both are numbers (e.g., `[4/3]`, `[16/10]`).
## Customizing Theme
Currently, the aspect ratio scale is hardcoded to standard Tailwind defaults (`auto`, `square`, `video`) and cannot be extended via `WindThemeData`.
To use custom ratios, we recommend using [Arbitrary Values](#arbitrary-values) or creating a custom parser if you need named utilities for specific ratios.
## Related Documentation
- [Sizing](./sizing.md)
- [Display](./display.md)
---
# Overflow
# Overflow
Utilities for controlling how an element handles content that is too large for the container.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Basic examples
WDiv(className: 'overflow-hidden')
WDiv(className: 'overflow-scroll')
WDiv(className: 'overflow-x-auto')
```
## Basic Usage
Use `overflow-{mode}` to control how content behaves when it exceeds the container's bounds.
### Hidden
Use `overflow-hidden` to clip any content that extends beyond the container's edges. This applies `Clip.hardEdge` to the widget.
```dart
WDiv(
className: 'overflow-hidden w-20 h-20 bg-white rounded-lg',
child: WDiv(className: 'w-40 h-40 bg-red-500'),
)
```
### Scroll
Use `overflow-scroll` to enable scrolling regardless of whether content overflows. This generally wraps the content in a `SingleChildScrollView`.
```dart
WDiv(
className: 'overflow-scroll w-64 h-64 bg-white',
child: LongContentWidget(),
)
```
### Auto
Use `overflow-auto` to enable scrolling only when content actually overflows the container bounds.
```dart
WDiv(
className: 'overflow-auto max-h-48 bg-gray-100',
child: LongContentWidget(),
)
```
### Visible
Use `overflow-visible` to allow content to spill out of the container. This applies `Clip.none` and prevents the container from introducing a scroll context.
```dart
WDiv(
className: 'overflow-visible',
child: LargeContentWidget(),
)
```
## Quick Reference
| Class | Flutter Equivalent | Description |
|:------|:-------------------|:------------|
| `overflow-auto` | `SingleChildScrollView` (conditional) | Scroll only if content overflows |
| `overflow-hidden` | `Clip.hardEdge` | Clip overflowing content |
| `overflow-visible` | `Clip.none` | Content flows outside container |
| `overflow-scroll` | `SingleChildScrollView` | Always enable scrolling |
## Variants
### Directional Overflow
Use `overflow-x-{mode}` and `overflow-y-{mode}` to control overflow on a specific axis independently.
```dart
WDiv(className: 'overflow-x-scroll overflow-y-hidden')
```
| Class | Description |
|:------|:------------|
| `overflow-x-auto` | Scroll horizontally if needed |
| `overflow-y-scroll` | Always scroll vertically |
| `overflow-x-hidden` | Clip horizontal overflow |
| `overflow-y-visible` | Allow vertical overflow |
## Responsive Design
Apply different overflow behaviors at different breakpoints using standard modifiers like `sm:`, `md:`, `lg:`, `xl:`, and `2xl:`.
```dart
// Scroll on mobile, but show full content on desktop
WDiv(className: 'overflow-scroll md:overflow-visible')
```
## Dark Mode
Change overflow behavior in dark mode using the `dark:` prefix.
```dart
WDiv(className: 'overflow-visible dark:overflow-hidden')
```
## Arbitrary Values
The overflow utility does not support arbitrary values. You must use one of the defined keywords (`hidden`, `visible`, `scroll`, `auto`).
## Customizing Theme
Overflow utilities are hardcoded behavior toggles and cannot be customized via `WindThemeData`.
## Related Documentation
- [Sizing](../layout/sizing.md)
- [Display](../layout/display.md)
---
# Z-Index
# Z-Index
Utilities for controlling the stack order of an element.
## Table of Contents
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Standard values
WDiv(className: 'z-0')
WDiv(className: 'z-50')
// Arbitrary values
WDiv(className: 'z-[100]')
WDiv(className: 'z-[-1]')
```
## Basic Usage
Use `z-{index}` to set the stack order of an element. This utility is primarily used with `Stack` widgets to determine which element appears on top.
```dart
Stack(
children: [
WDiv(className: 'z-0 bg-blue-500 w-20 h-20'),
WDiv(className: 'z-10 bg-red-500 w-20 h-20 ml-10 mt-10'),
WDiv(className: 'z-20 bg-green-500 w-20 h-20 ml-20 mt-20'),
],
)
```
## Quick Reference
| Class | Value | Description |
|:------|:------|:------------|
| `z-0` | 0 | Stack level 0 |
| `z-10` | 10 | Stack level 10 |
| `z-20` | 20 | Stack level 20 |
| `z-30` | 30 | Stack level 30 |
| `z-40` | 40 | Stack level 40 |
| `z-50` | 50 | Stack level 50 |
| `z-auto` | `null` | Resets stack level (default) |
## Variants
### Hover & Focus
Control the stack order on interaction. This is useful for "pop-out" effects where an item should rise above others when hovered.
```dart
WDiv(className: 'z-0 hover:z-50 bg-white shadow-sm hover:shadow-xl')
```
## Responsive Design
Apply different z-indices at specific breakpoints using the `sm:`, `md:`, `lg:`, `xl:`, and `2xl:` prefixes.
```dart
// z-0 on mobile, z-50 on medium screens and up
WDiv(className: 'z-0 md:z-50')
```
## Dark Mode
Use the `dark:` prefix to apply specific z-indices when the application is in dark mode.
```dart
WDiv(className: 'z-10 dark:z-20')
```
## Arbitrary Values
Use square brackets `[]` to apply custom z-index values that aren't part of the theme. This supports both positive and negative integers.
```dart
// Custom positive value
WDiv(className: 'z-[100]')
// Custom negative value
WDiv(className: 'z-[-5]')
```
## Customizing Theme
To extend or override the default z-index scale, modify the `zIndices` property in `WindThemeData`.
```dart
WindThemeData(
zIndices: {
// Override defaults
'0': 0,
'50': 50,
// Add custom values
'100': 100,
'modal': 9999,
'popover': 5000,
},
)
```
Usage with custom theme:
```dart
WDiv(className: 'z-modal') // Applies z-index: 9999
WDiv(className: 'z-popover') // Applies z-index: 5000
```
## Related Documentation
- [Position](/doc/layout/position.md)
- [Display](/doc/layout/display.md)
- [WindTheme](/doc/core-concepts/theming.md)
---
# Font Family
# Font Family
Utilities for controlling the typeface of text.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WText('Sans-serif text', className: 'font-sans')
WText('Serif text', className: 'font-serif')
WText('Monospace text', className: 'font-mono')
```
## Basic Usage
Use `font-{family}` utilities to apply a font stack to an element.
### Sans-serif
Use `font-sans` to apply a web-safe sans-serif font stack. This is the default font family for Wind.
```dart
WText(
'The quick brown fox jumps over the lazy dog.',
className: 'font-sans text-lg',
)
```
### Serif
Use `font-serif` to apply a serif font stack, ideal for long-form text or editorial designs.
```dart
WText(
'The quick brown fox jumps over the lazy dog.',
className: 'font-serif text-lg',
)
```
### Monospace
Use `font-mono` to apply a monospace font stack, perfect for code snippets or technical data.
```dart
WText(
'const pi = 3.14159;',
className: 'font-mono text-sm bg-gray-100 p-2 rounded',
)
```
## Quick Reference
| Class | Font Family | Description |
|:------|:------------|:------------|
| `font-sans` | `ui-sans-serif, system-ui, ...` | Default sans-serif stack. |
| `font-serif` | `ui-serif, Georgia, ...` | Default serif stack. |
| `font-mono` | `ui-monospace, SFMono-Regular, ...` | Default monospace stack. |
## Variants
Hover and focus states can also change the font family, though this is less common.
```dart
WText(
'Hover me to switch fonts',
className: 'font-sans hover:font-serif transition-all',
)
```
## Responsive Design
Apply different font families at different breakpoints using standard responsive prefixes.
```dart
WText(
'Responsive Typography',
className: 'font-sans md:font-serif lg:font-mono',
)
```
## Dark Mode
You can enforce specific font families in dark mode using the `dark:` prefix.
```dart
WText(
'Code Snippet',
className: 'font-sans dark:font-mono',
)
```
## Arbitrary Values
If you need a specific font family that isn't in your theme, use bracket notation. This is useful for one-off fonts.
```dart
WText(
'Custom Branding',
className: 'font-[Roboto]',
)
```
## Customizing Theme
To add your own font families (like `font-display` or `font-body`), update `WindThemeData` in your app root.
```dart
WindTheme(
data: WindThemeData(
fontFamilies: {
'sans': 'Inter', // Overrides default font-sans
'display': 'Oswald', // Adds new font-display class
'body': 'Open Sans', // Adds new font-body class
},
),
child: MaterialApp(home: MyApp()),
)
```
Now you can use these classes in your widgets:
```dart
WText('Headlines', className: 'font-display text-4xl')
WText('Body text', className: 'font-body')
```
> [!NOTE]
> When using custom fonts (like Google Fonts), ensure the font assets are properly included in your `pubspec.yaml` or loaded via a package like `google_fonts`.
## Related Documentation
- [Font Size](./font-size.md)
- [Font Weight](./font-weight.md)
- [Letter Spacing](./letter-spacing.md)
---
# Font Size
# Font Size
Utilities for controlling the font size of an element.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WText('Small text', className: 'text-sm')
WText('Large text', className: 'text-lg')
WText('Extra large', className: 'text-xl')
```
## Basic Usage
Use `text-{size}` to set the font size of an element.
```dart
WText(
'The quick brown fox jumps over the lazy dog.',
className: 'text-base',
)
WText(
'Main Heading',
className: 'text-4xl font-bold',
)
```
## Quick Reference
| Class | Size | Pixel Value |
|:------|:-----|:------------|
| `text-xs` | 0.75rem | 12px |
| `text-sm` | 0.875rem | 14px |
| `text-base` | 1rem | 16px |
| `text-lg` | 1.125rem | 18px |
| `text-xl` | 1.25rem | 20px |
| `text-2xl` | 1.5rem | 24px |
| `text-3xl` | 1.875rem | 30px |
| `text-4xl` | 2.25rem | 36px |
| `text-5xl` | 3rem | 48px |
| `text-6xl` | 3.75rem | 60px |
> For sizes beyond `text-6xl`, use the arbitrary-value syntax (`text-[80px]`) or extend `WindThemeData.fontSizes` with additional entries.
## Variants
### Line Height Modifier
You can set the line height simultaneously by adding a forward slash and a line-height value to any font size utility.
This supports both named line heights (from your theme) and numeric spacing values.
```dart
// Named line height (text-lg + leading-loose)
WText('Loose text', className: 'text-lg/loose')
// Numeric spacing (text-base + 28px line height)
// 7 * 4px = 28px
WText('Spaced text', className: 'text-base/7')
// Arbitrary line height
WText('Precise text', className: 'text-xl/[32px]')
```
| Class | Size | Line Height |
|:------|:-----|:------------|
| `text-sm/6` | 14px | 24px |
| `text-base/7` | 16px | 28px |
| `text-lg/relaxed` | 18px | 1.625 (factor) |
## Responsive Design
Apply different font sizes at different breakpoints using the standard `sm:`, `md:`, `lg:`, `xl:`, and `2xl:` prefixes.
```dart
WText(
'Responsive Text',
className: 'text-sm md:text-base lg:text-xl',
)
```
## Dark Mode
Use the `dark:` prefix to apply different font sizes when the application is in dark mode.
```dart
WText(
'Dynamic Sizing',
className: 'text-base dark:text-lg',
)
```
## Arbitrary Values
If the built-in scale doesn't meet your needs, use bracket notation to apply custom values directly.
```dart
// Exact pixel value
WText('15px text', className: 'text-[15px]')
// Rem value (treated as pixels if no unit provided, or parsed explicitly)
WText('1.5rem text', className: 'text-[1.5rem]')
```
## Customizing Theme
To extend or override the default font size scale, modify the `fontSizes` property in `WindThemeData`.
```dart
WindThemeData(
fontSizes: {
// Override existing
'xs': 10.0,
// Add new custom size
'mega': 100.0,
},
)
```
Usage:
```dart
WText('Tiny text', className: 'text-xs') // 10.0
WText('Huge text', className: 'text-mega') // 100.0
```
## Related Documentation
- [Font Weight](./font-weight.md)
- [Line Height](./line-height.md)
- [Text Color](./text-color.md)
---
# Font Weight
# Font Weight
Utilities for controlling the font weight of an element.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WDiv(
className: 'flex flex-col gap-4',
children: [
WText('The quick brown fox', className: 'font-light'),
WText('The quick brown fox', className: 'font-normal'),
WText('The quick brown fox', className: 'font-bold'),
],
)
```
## Basic Usage
Control the font weight of an element using the `font-{weight}` utilities.
```dart
WText('Most text uses font-normal', className: 'font-normal')
WText('Important text uses font-bold', className: 'font-bold')
```
## Quick Reference
| Class | Weight | Description |
|:------|:-------|:------------|
| `font-thin` | 100 | Thin |
| `font-extralight` | 200 | Extra Light |
| `font-light` | 300 | Light |
| `font-normal` | 400 | Normal |
| `font-medium` | 500 | Medium |
| `font-semibold` | 600 | Semi Bold |
| `font-bold` | 700 | Bold |
| `font-extrabold` | 800 | Extra Bold |
| `font-black` | 900 | Black |
## Variants
### Font Style
Control the font style of an element using the `italic` and `not-italic` utilities.
```dart
WText('This text is italicized.', className: 'italic')
WText('This text is normal.', className: 'not-italic')
```
| Class | Style | Description |
|:------|:------|:------------|
| `italic` | `FontStyle.italic` | Renders text in italics |
| `not-italic` | `FontStyle.normal` | Renders text normally (useful for resetting) |
## Responsive Design
Apply different font weights at specific breakpoints using standard responsive prefixes.
```dart
// Normal on mobile, bold on medium screens and up
WText('Responsive Weight', className: 'font-normal md:font-bold')
```
## Dark Mode
Adjust font weight based on the theme brightness. This is useful for maintaining legibility against dark backgrounds, where lighter weights might appear too thin.
```dart
// Lighter weight in dark mode for better readability
WText('Adaptive Weight', className: 'font-medium dark:font-normal')
```
## Arbitrary Values
If you need a specific font weight that isn't included in your theme, use square bracket notation.
```dart
// Maps to the nearest standard FontWeight (e.g. 700 -> Bold)
WText('Custom Weight', className: 'font-[700]')
WText('Variable Weight', className: 'font-[550]')
```
## Customizing Theme
You can customize the `fontWeights` scale in your `WindThemeData` configuration.
```dart
WindTheme(
data: WindThemeData(
fontWeights: {
'hairline': FontWeight.w100,
'heavy': FontWeight.w900,
// Override default
'bold': FontWeight.w600,
},
),
child: MyApp(),
)
```
## Related Documentation
- [Font Size](./font-size.md)
- [Font Family](./font-family.md)
- [Text Color](./text-color.md)
---
# Line Height
# Line Height
Utilities for controlling the leading (line height) of an element.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Relative line-heights
WText('Leading none', className: 'leading-none')
WText('Leading loose', className: 'leading-loose')
// Fixed line-heights
WText('Leading 6', className: 'leading-6')
```
## Basic Usage
Use `leading-{value}` to control the line height of an element. Relative line heights (like `normal`, `loose`) depend on the font size, while fixed line heights (like `leading-6`) set a specific height independent of the font size.
```dart
WDiv(
className: 'max-w-md mx-auto bg-white rounded-xl shadow-md overflow-hidden md:max-w-2xl',
child: WDiv(
className: 'p-8',
child: WText(
'The quick brown fox jumps over the lazy dog.',
className: 'leading-loose text-gray-900',
),
),
)
```
> [!NOTE]
> You can also set line height directly within the font size utility, e.g., `text-lg/7` or `text-lg/loose`. See [Font Size](./font-size.md) for details.
## Quick Reference
| Class | Value | Description |
|:--- |:--- |:--- |
| `leading-none` | 1 | Relative height |
| `leading-tight` | 1.25 | Relative height |
| `leading-snug` | 1.375 | Relative height |
| `leading-normal` | 1.5 | Relative height |
| `leading-relaxed` | 1.625 | Relative height |
| `leading-loose` | 2 | Relative height |
| `leading-3` | 0.75rem | Fixed height (12px) |
| `leading-4` | 1rem | Fixed height (16px) |
| `leading-5` | 1.25rem | Fixed height (20px) |
| `leading-6` | 1.5rem | Fixed height (24px) |
| `leading-7` | 1.75rem | Fixed height (28px) |
| `leading-8` | 2rem | Fixed height (32px) |
| `leading-9` | 2.25rem | Fixed height (36px) |
| `leading-10` | 2.5rem | Fixed height (40px) |
## Responsive Design
Apply different line height values at different breakpoints using the standard `sm:`, `md:`, `lg:`, `xl:`, and `2xl:` prefixes.
```dart
WText(
'Responsive leading',
className: 'leading-none md:leading-normal lg:leading-loose',
)
```
## Dark Mode
Use the `dark:` prefix to apply different line heights when the application is in dark mode.
```dart
WText(
'Dark mode leading',
className: 'leading-normal dark:leading-loose',
)
```
## Arbitrary Values
If the built-in scale doesn't meet your needs, use bracket notation to apply custom values directly.
```dart
// Fixed pixel value
WText('Custom px', className: 'leading-[24px]')
// Relative value (unitless)
WText('Custom relative', className: 'leading-[1.8]')
// Rem value
WText('Custom rem', className: 'leading-[3rem]')
```
## Customizing Theme
To extend or override the default line height scale, modify the `leading` property in `WindThemeData`.
```dart
WindThemeData(
leading: {
'tighter': 1.1,
'double': 2.0,
// Overriding existing
'loose': 2.5,
},
)
```
## Related Documentation
- [Font Size](./font-size.md) - Setting font size and line height together
- [Letter Spacing](./letter-spacing.md) - Controlling tracking (letter spacing)
---
# Letter Spacing
# Letter Spacing
Utilities for controlling the tracking (letter spacing) of an element.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WDiv(
className: 'flex flex-col gap-4',
children: [
WText('Tight letter spacing', className: 'tracking-tight'),
WText('Normal letter spacing', className: 'tracking-normal'),
WText('Wide letter spacing', className: 'tracking-wide'),
],
)
```
## Basic Usage
Use `tracking-{size}` to control the letter spacing of an element.
```dart
WText(
'The quick brown fox jumps over the lazy dog.',
className: 'tracking-wide',
)
```
## Quick Reference
| Class | Value | Description |
|:------|:------|:------------|
| `tracking-tighter` | -2.0 | Tighter than default spacing |
| `tracking-tight` | -1.0 | Slightly tighter spacing |
| `tracking-normal` | 0.0 | Standard spacing (default) |
| `tracking-wide` | 1.0 | Slightly wider spacing |
| `tracking-wider` | 2.0 | Wider than default spacing |
| `tracking-widest` | 4.0 | Widest available spacing |
## Responsive Design
Apply different letter spacing at different breakpoints using the standard `sm:`, `md:`, `lg:`, `xl:`, and `2xl:` prefixes.
```dart
WText(
'Responsive Tracking',
className: 'tracking-normal md:tracking-wide lg:tracking-widest',
)
```
## Dark Mode
Use the `dark:` prefix to apply different tracking when the application is in dark mode.
```dart
WText(
'Dark Mode Tracking',
className: 'tracking-normal dark:tracking-wide',
)
```
## Arbitrary Values
If the built-in scale doesn't meet your needs, use bracket notation to apply custom values directly.
```dart
// Sets letterSpacing to 3px
WText(
'Custom Spacing',
className: 'tracking-[3px]',
)
```
## Customizing Theme
To extend or override the default tracking scale, modify the `tracking` property in `WindThemeData`.
```dart
WindThemeData(
tracking: {
'extra-wide': 6.0,
'mega': 8.0,
},
)
```
Then use it in your code:
```dart
WText('Mega Spacing', className: 'tracking-mega')
```
## Related Documentation
- [Font Size](./font-size.md)
- [Line Height](./line-height.md)
- [Font Weight](./font-weight.md)
---
# Text Align
# Text Align
Utilities for controlling the alignment of text.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Basic alignment examples
WText('Left aligned', className: 'text-left')
WText('Center aligned', className: 'text-center')
WText('Right aligned', className: 'text-right')
```
## Basic Usage
Use `text-left`, `text-center`, `text-right`, and `text-justify` to control the text alignment of an element.
```dart
WDiv(
className: 'flex flex-col gap-4',
children: [
WText('Left aligned text.', className: 'text-left'),
WText('Center aligned text.', className: 'text-center'),
WText('Right aligned text.', className: 'text-right'),
WText('Justified text.', className: 'text-justify'),
],
)
```
## Quick Reference
| Class | Value | Description |
|:------|:------|:------------|
| `text-left` | `TextAlign.left` | Align text to the left. |
| `text-center` | `TextAlign.center` | Align text to the center. |
| `text-right` | `TextAlign.right` | Align text to the right. |
| `text-justify` | `TextAlign.justify` | Stretch lines of text to equal width. |
| `text-start` | `TextAlign.start` | Align text to the start (direction-aware). |
| `text-end` | `TextAlign.end` | Align text to the end (direction-aware). |
## Responsive Design
Apply different alignment at different breakpoints using the standard `sm:`, `md:`, `lg:`, `xl:`, and `2xl:` prefixes.
```dart
// Center on mobile, left on medium screens and up
WText(
'Responsive Alignment',
className: 'text-center md:text-left',
)
```
## Dark Mode
Use the `dark:` prefix to apply different alignment in dark mode. While less common for alignment than colors, it is fully supported.
```dart
WText('Dark mode alignment', className: 'text-left dark:text-center')
```
## Arbitrary Values
The text alignment utility does not support arbitrary values because it maps directly to Flutter's `TextAlign` enum.
## Customizing Theme
Text alignment values are hardcoded to map to Flutter's `TextAlign` enum and cannot be customized via `WindThemeData`.
## Related Documentation
- [Font Size](./font-size.md)
- [Font Weight](./font-weight.md)
- [Line Height](./line-height.md)
---
# Text Color
# Text Color
Utilities for controlling the text color of an element.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Opacity Modifier](#opacity-modifier)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Basic text colors
WText('Error', className: 'text-red-500')
WText('Success', className: 'text-green-600')
WText('Info', className: 'text-blue-500')
```
## Basic Usage
Control the text color of an element using the `text-{color}-{shade}` utilities.
```dart
WDiv(
className: 'p-4 bg-white rounded-lg shadow-sm',
children: [
WText('The quick brown fox', className: 'text-slate-900 font-bold text-xl'),
WText('Jumps over the lazy dog', className: 'text-slate-500'),
],
)
```
## Quick Reference
Wind includes the standard Tailwind color palette. Here are some examples:
| Class | Value | Description |
|:------|:------|:------------|
| `text-slate-500` | #64748b | Slate text color |
| `text-gray-500` | #6b7280 | Gray text color |
| `text-red-500` | #ef4444 | Red text color |
| `text-blue-600` | #2563eb | Blue text color |
| `text-white` | #ffffff | White text |
| `text-black` | #000000 | Black text |
| `text-transparent` | transparent | Transparent text |
## Opacity Modifier
Control the opacity of the text color using the color opacity modifier. Add a forward slash and the opacity percentage value to any color utility.
```dart
WDiv(
className: 'flex flex-col gap-2',
children: [
WText('Text opacity 100%', className: 'text-blue-500'),
WText('Text opacity 75%', className: 'text-blue-500/75'),
WText('Text opacity 50%', className: 'text-blue-500/50'),
WText('Text opacity 25%', className: 'text-blue-500/25'),
],
)
```
## Responsive Design
Apply different text colors at specific breakpoints using standard responsive prefixes.
```dart
WText(
'Responsive Text Color',
className: 'text-blue-500 md:text-green-500 lg:text-red-500',
)
```
## Dark Mode
Use the `dark:` prefix to apply specific text colors when dark mode is active.
```dart
WDiv(
className: 'bg-white dark:bg-slate-900 p-6',
child: WText(
'Dark Mode Support',
className: 'text-slate-900 dark:text-white',
),
)
```
## Arbitrary Values
If you need a specific color that isn't in your theme, use square brackets to generate a property on the fly using a hex code.
```dart
WText(
'Custom Hex Color',
className: 'text-[#50d71e]',
)
```
## Customizing Theme
To add your own colors, modify the `colors` property in `WindThemeData`.
```dart
WindThemeData(
colors: {
// Add custom color map
'brand': {
'500': Color(0xFF1E40AF),
},
// Extend existing map
...WindThemeData.defaultColors,
'tahiti': {
'100': Color(0xFFcffafe),
'900': Color(0xFF164e63),
},
},
)
```
Then use them in your classes:
```dart
WText('My Brand Color', className: 'text-brand-500')
WText('Tahiti Color', className: 'text-tahiti-900')
```
## Related Documentation
- [Background Color](../styling/background-color.md)
- [Text Decoration Color](text-decoration-color.md)
---
# Text Transform
# Text Transform
Utilities for controlling the capitalization and wrapping of text.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Whitespace & Wrapping](#whitespace--wrapping)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Uppercase
WText('Hello World', className: 'uppercase')
// Capitalize
WText('hello world', className: 'capitalize')
// Prevent wrapping
WText('Long text that should not wrap...', className: 'whitespace-nowrap')
```
## Basic Usage
Use `uppercase` and `lowercase` to force text casing. `capitalize` converts the first character of each word to uppercase.
```dart
WDiv(
className: 'flex flex-col gap-4',
children: [
WText('uppercase text', className: 'uppercase'), // UPPERCASE TEXT
WText('LOWERCASE TEXT', className: 'lowercase'), // lowercase text
WText('capitalize this text', className: 'capitalize'), // Capitalize This Text
WText('Normal Case Text', className: 'normal-case'), // Normal Case Text
],
)
```
## Quick Reference
| Class | Transform | Description |
|:------|:----------|:------------|
| `uppercase` | Uppercase | Converts all text to uppercase. |
| `lowercase` | Lowercase | Converts all text to lowercase. |
| `capitalize` | Capitalize | Capitalizes the first letter of each word. |
| `normal-case` | None | Resets text transformation (useful for overrides). |
## Whitespace & Wrapping
Control how text handles whitespace and wrapping using `whitespace-` utilities.
```dart
// Prevent text from wrapping
WText(
'This is a long sentence that will not wrap to the next line.',
className: 'whitespace-nowrap',
)
```
| Class | Wrap | Description |
|:------|:-----|:------------|
| `whitespace-normal` | Yes | Allow text to wrap normally (default). |
| `whitespace-nowrap` | No | Prevent text from wrapping. |
| `text-wrap` | Yes | Alias for `whitespace-normal`. |
| `text-nowrap` | No | Alias for `whitespace-nowrap`. |
| `text-balance` | Balance | Balances text across lines for better readability. |
## Responsive Design
Apply transforms or whitespace rules conditionally at different breakpoints.
```dart
// Uppercase on mobile, normal case on tablet+
WText('Responsive Text', className: 'uppercase md:normal-case')
// Wrap on mobile, no-wrap on large screens
WText('Responsive Wrap', className: 'whitespace-normal lg:whitespace-nowrap')
```
## Dark Mode
While text transforms rarely change based on theme, you can apply them conditionally if needed.
```dart
WText('Dark Mode Text', className: 'normal-case dark:uppercase')
```
## Customizing Theme
Text transform and whitespace utilities are hardcoded in the parser and **cannot be customized** via `WindThemeData`.
## Related Documentation
- [Text Overflow](./text-overflow.md)
- [Font Weight](./font-weight.md)
- [Letter Spacing](./letter-spacing.md)
---
# Text Decoration
# Text Decoration
Utilities for controlling the decoration of text.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Basic decoration
WText('Underline', className: 'underline')
// With color, style, and thickness
WText('Styled', className: 'underline decoration-blue-500 decoration-wavy decoration-2')
```
## Basic Usage
Use `underline`, `overline`, or `line-through` to decorate text. Use `no-underline` to remove existing decorations.
```dart
WText('The quick brown fox...', className: 'underline')
WText('The quick brown fox...', className: 'overline')
WText('The quick brown fox...', className: 'line-through')
WText('Link without underline', className: 'no-underline')
```
## Quick Reference
| Class | Properties | Description |
|:------|:-----------|:------------|
| `underline` | `decoration: underline` | Adds an underline. |
| `overline` | `decoration: overline` | Adds an overline. |
| `line-through` | `decoration: line-through` | Adds a strikethrough. |
| `no-underline` | `decoration: none` | Removes decorations. |
## Variants
### Decoration Color
Use the `decoration-{color}` utilities to change the color of the text decoration.
```dart
WText('Error', className: 'underline decoration-red-500')
WText('Success', className: 'underline decoration-green-600')
```
### Decoration Style
Use the `decoration-{style}` utilities to change the style of the text decoration.
```dart
WText('Solid', className: 'underline decoration-solid')
WText('Double', className: 'underline decoration-double')
WText('Dotted', className: 'underline decoration-dotted')
WText('Dashed', className: 'underline decoration-dashed')
WText('Wavy', className: 'underline decoration-wavy')
```
| Class | Description |
|:------|:------------|
| `decoration-solid` | Sets the decoration style to solid. |
| `decoration-double` | Sets the decoration style to double. |
| `decoration-dotted` | Sets the decoration style to dotted. |
| `decoration-dashed` | Sets the decoration style to dashed. |
| `decoration-wavy` | Sets the decoration style to wavy. |
### Decoration Thickness
Use the `decoration-{width}` utilities to change the thickness of the text decoration.
```dart
WText('1px', className: 'underline decoration-1')
WText('2px', className: 'underline decoration-2')
WText('4px', className: 'underline decoration-4')
WText('8px', className: 'underline decoration-8')
```
## Responsive Design
Apply different decoration utilities at different breakpoints using standard responsive modifiers.
```dart
WText('Responsive decoration', className: 'no-underline md:underline')
```
## Dark Mode
Use the `dark:` prefix to apply different decoration styles in dark mode.
```dart
WText('Link', className: 'text-gray-900 underline decoration-gray-900 dark:text-white dark:decoration-white')
```
## Arbitrary Values
Use bracket notation for custom decoration colors or thickness.
```dart
// Custom color
WText('Brand underline', className: 'underline decoration-[#50d71e]')
// Custom thickness
WText('3px underline', className: 'underline decoration-[3px]')
```
## Customizing Theme
### Decoration Color
Decoration colors use your theme's `colors` configuration.
```dart
WindThemeData(
colors: {
'brand': {
'500': Color(0xFF1E88E5),
},
},
)
```
## Related Documentation
- [Text Color](./text-color.md)
- [Font Weight](./font-weight.md)
---
# Text Overflow
# Text Overflow
Utilities for controlling how text behaves when it overflows its container.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Truncate](#truncate)
- [Ellipsis & Clip](#ellipsis--clip)
- [Line Clamp](#line-clamp)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Related Documentation](#related-documentation)
```dart
// Basic truncation
WText(
'Long text that should be truncated...',
className: 'truncate',
)
// Limit to specific lines
WText(
'Multi-line text block...',
className: 'line-clamp-3',
)
```
## Basic Usage
Use `truncate` to prevent text from wrapping and truncate overflowing text with an ellipsis (...) if needed. This is the most common way to handle overflow for single-line text.
```dart
WDiv(
className: 'w-64',
child: WText(
'The quick brown fox jumps over the lazy dog.',
className: 'truncate',
),
)
```
## Quick Reference
| Class | Properties | Description |
|:------|:-----------|:------------|
| `truncate` | `maxLines: 1`, `softWrap: false`, `overflow: ellipsis` | Truncate text with an ellipsis on a single line. |
| `text-ellipsis` | `overflow: ellipsis` | Use an ellipsis for overflow. |
| `text-clip` | `overflow: clip` | Clip overflowing text (no ellipsis). |
| `line-clamp-{n}` | `maxLines: n`, `overflow: ellipsis` | Limit text to n lines. |
| `line-clamp-none` | `maxLines: null` | Remove line limits. |
## Truncate
The `truncate` utility is a shorthand that sets three properties at once:
1. Sets `maxLines` to 1
2. Disables `softWrap`
3. Sets `overflow` to `TextOverflow.ellipsis`
```dart
WText(
'Long text that will be cut off with an ellipsis if it gets too long',
className: 'truncate',
)
```
## Ellipsis & Clip
Use `text-ellipsis` or `text-clip` to control the visual overflow behavior specifically.
- **text-ellipsis**: Renders an ellipsis (...) when text overflows.
- **text-clip**: Simply cuts off the text at the boundary.
```dart
// Ellipsis (default for truncate/line-clamp)
WText('...', className: 'text-ellipsis')
// Clip (hard cut)
WText('...', className: 'text-clip')
```
## Line Clamp
Use `line-clamp-{n}` to limit text to a specific number of lines. This automatically sets the overflow style to ellipsis.
Supported values are any integer number (e.g., `line-clamp-2`, `line-clamp-5`) and `line-clamp-none`.
```dart
WText(
'This is a long paragraph that needs to be clamped to a specific number of lines...',
className: 'line-clamp-3',
)
// Resetting line clamp
WText(
'...',
className: 'line-clamp-3 lg:line-clamp-none',
)
```
## Responsive Design
Apply different overflow utilities at different breakpoints using standard responsive prefixes.
```dart
// Truncate on mobile, allow wrapping on desktop
WText(
'Responsive text behavior...',
className: 'truncate md:whitespace-normal',
)
// 2 lines on mobile, 4 lines on tablet, unlimited on desktop
WText(
'Responsive line clamping...',
className: 'line-clamp-2 md:line-clamp-4 lg:line-clamp-none',
)
```
## Dark Mode
While overflow utilities don't visually change in dark mode, you can still apply them conditionally using the `dark:` prefix if your design requires different text handling in dark mode.
```dart
WText(
'...',
className: 'truncate dark:line-clamp-2',
)
```
## Arbitrary Values
> [!NOTE]
> Arbitrary values (bracket syntax) are not currently supported for `text-` overflow or `line-clamp-` utilities.
>
> `line-clamp-{n}` supports any integer number directly (e.g., `line-clamp-7`), so bracket syntax is generally not needed.
## Customizing Theme
These utilities do not use theme variables. `line-clamp` accepts numeric values directly, and overflow types are static.
## Related Documentation
- [Word Break & Whitespace](./whitespace.md)
- [Font Size](./font-size.md)
---
# Background Color
# Background Color
Utilities for controlling an element's background color.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Opacity Modifier](#opacity-modifier)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Solid color
WDiv(className: 'bg-blue-500 h-12 w-12 rounded')
// With opacity
WDiv(className: 'bg-blue-500/50 h-12 w-12 rounded')
// White and Black
WDiv(className: 'bg-white h-12 w-12 rounded')
WDiv(className: 'bg-black h-12 w-12 rounded')
```
## Basic Usage
Use `bg-{color}-{shade}` to set the background color of an element. Wind includes a comprehensive palette of colors (slate, gray, red, blue, etc.) with shades from 50 to 950.
```dart
WDiv(
className: 'bg-blue-500 p-4 rounded-lg',
child: WText('Blue Background', className: 'text-white'),
)
```
You can also use standalone colors like `bg-white`, `bg-black`, and `bg-transparent`.
```dart
WDiv(className: 'bg-white shadow-md p-6')
WDiv(className: 'bg-transparent border border-gray-300')
```
## Quick Reference
| Class | Description |
|:------|:------------|
| `bg-{color}-{shade}` | Sets background color (e.g., `bg-red-500`, `bg-slate-900`). |
| `bg-white` | Sets background to white. |
| `bg-black` | Sets background to black. |
| `bg-transparent` | Sets background to transparent. |
| `bg-current` | Sets background to the current text color. |
## Opacity Modifier
Control the opacity of a background color using the slash modifier `/{opacity}`. The value ranges from 0 to 100.
```dart
WDiv(className: 'bg-blue-500/100') // 100% (Default)
WDiv(className: 'bg-blue-500/75') // 75%
WDiv(className: 'bg-blue-500/50') // 50%
WDiv(className: 'bg-blue-500/25') // 25%
```
## Responsive Design
Change the background color at specific breakpoints using standard responsive prefixes (`sm:`, `md:`, `lg:`, `xl:`, `2xl:`).
```dart
// Red on mobile, Blue on medium screens and up
WDiv(className: 'bg-red-500 md:bg-blue-500 h-32 w-full')
```
## Dark Mode
Use the `dark:` prefix to apply a different background color when dark mode is active.
```dart
WDiv(
className: 'bg-white dark:bg-slate-900 p-6 rounded-lg',
child: WText('Adaptive Background', className: 'text-gray-900 dark:text-white'),
)
```
## Arbitrary Values
If you need a specific color not in your theme, use square bracket notation `bg-[#RRGGBB]`.
```dart
WDiv(className: 'bg-[#1da1f2] text-white p-4') // Twitter Blue
WDiv(className: 'bg-[#ff0000]') // Bright Red
```
## Customizing Theme
To add your own colors, modify the `colors` map in `WindThemeData`.
```dart
WindThemeData(
colors: {
// Add a custom brand color
'brand': {
'500': Color(0xFF1DA1F2),
'600': Color(0xFF1A91DA),
},
// Extend existing palette
'gray': {
...WindThemeData.defaultColors['gray']!,
'1000': Color(0xFF0F0F0F),
},
},
)
```
Now you can use `bg-brand-500` or `bg-gray-1000`.
## Related Documentation
- [Background Gradient](./background-gradient.md)
- [Background Image](./background-image.md)
- [Text Color](./text-color.md)
- [Border Color](./border-color.md)
---
# Background Gradient
# Background Gradient
Utilities for creating linear gradients with customizable directions and color stops.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Basic gradient from cyan to blue
WDiv(className: 'bg-gradient-to-r from-cyan-500 to-blue-500')
// Three-color gradient
WDiv(className: 'bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500')
```
## Basic Usage
To create a gradient, you need to specify a direction (e.g., `bg-gradient-to-r`) and at least a starting color (`from-*`). Wind will create a linear gradient based on these parameters.
```dart
WDiv(
className: 'h-24 bg-gradient-to-r from-cyan-500 to-blue-500',
child: WText('Gradient Background'),
)
```
If you only provide a `from` color, the gradient will fade to transparent by default. If you provide `to` but no `from`, it will also handle it gracefully, but standard usage typically defines both or relies on the fade-to-transparent behavior.
## Quick Reference
| Class | Description |
|:------|:------------|
| `bg-gradient-to-t` | Gradient to top |
| `bg-gradient-to-tr` | Gradient to top right |
| `bg-gradient-to-r` | Gradient to right |
| `bg-gradient-to-br` | Gradient to bottom right |
| `bg-gradient-to-b` | Gradient to bottom |
| `bg-gradient-to-bl` | Gradient to bottom left |
| `bg-gradient-to-l` | Gradient to left |
| `bg-gradient-to-tl` | Gradient to top left |
| `from-{color}` | Starting color |
| `via-{color}` | Middle color |
| `to-{color}` | Ending color |
## Variants
### Gradient Stops
Use `from-*`, `via-*`, and `to-*` utilities to define the color stops of your gradient.
```dart
// Start color only (fades to transparent)
WDiv(className: 'bg-gradient-to-r from-green-400')
// Start and end colors
WDiv(className: 'bg-gradient-to-r from-green-400 to-blue-500')
// Start, middle, and end colors
WDiv(className: 'bg-gradient-to-r from-green-400 via-blue-500 to-purple-600')
```
### Color Opacity
You can control the opacity of gradient colors using the `/opacity` modifier.
```dart
WDiv(className: 'bg-gradient-to-r from-red-500/50 to-red-500/0')
```
## Responsive Design
Apply different gradient directions or colors at different breakpoints using standard responsive prefixes.
```dart
WDiv(className: 'bg-gradient-to-r md:bg-gradient-to-b from-blue-500 to-green-500')
```
## Dark Mode
Use `dark:` to specify different gradient colors for dark mode.
```dart
WDiv(className: 'bg-gradient-to-r from-slate-100 to-slate-200 dark:from-slate-800 dark:to-slate-900')
```
## Arbitrary Values
If you need specific hex colors that aren't in your theme, use square bracket notation.
```dart
WDiv(className: 'bg-gradient-to-r from-[#1da1f2] to-[#1a91da]')
```
## Customizing Theme
Gradient colors use your theme's color palette. To add new colors, update the `colors` key in `WindThemeData`.
```dart
WindThemeData(
colors: {
'brand': {
'500': Color(0xFF1DA1F2),
'600': Color(0xFF1A91DA),
},
},
)
```
Then use them in your gradients:
```dart
WDiv(className: 'bg-gradient-to-r from-brand-500 to-brand-600')
```
## Related Documentation
- [Background Color](./background-color.md)
- [Background Image](./background-image.md)
---
# Background Image
# Background Image
Utilities for controlling background images, sizing, positioning, and repeat behavior.
```dart
WDiv(
className: 'bg-[url(https://example.com/hero.jpg)] bg-cover bg-center h-64 w-full rounded-lg',
child: WText('Hero Section', className: 'text-white font-bold'),
)
```
## Basic Usage
### Setting a Background Image
Use the `bg-[url(...)]` utility to set a background image. Wind supports network URLs, local files, and assets.
```dart
// Network image
WDiv(className: 'bg-[url(https://example.com/image.jpg)]')
// Asset image (shorthand for assets/)
WDiv(className: 'bg-[url(images/hero.png)]')
// Asset with explicit prefixes
WDiv(className: 'bg-[url(~/images/hero.png)]') // Resolves to assets/images/hero.png
WDiv(className: 'bg-[url(@/images/hero.png)]') // Resolves to assets/images/hero.png
```
## Quick Reference
| Class | Description | Flutter Equivalent |
|:--- |:--- |:--- |
| `bg-[url(...)]` | Sets the background image | `DecorationImage(image: ...)` |
| `bg-cover` | Scales image to cover container | `fit: BoxFit.cover` |
| `bg-contain` | Scales image to fit container | `fit: BoxFit.contain` |
| `bg-center` | Centers the image | `alignment: Alignment.center` |
| `bg-top` | Aligns image to top | `alignment: Alignment.topCenter` |
| `bg-bottom` | Aligns image to bottom | `alignment: Alignment.bottomCenter` |
| `bg-left` | Aligns image to left | `alignment: Alignment.centerLeft` |
| `bg-right` | Aligns image to right | `alignment: Alignment.centerRight` |
| `bg-no-repeat` | Prevents image repetition | `repeat: ImageRepeat.noRepeat` |
## Variants
### Size
Control how the background image fills its container using `bg-cover` or `bg-contain`.
```dart
WDiv(className: 'bg-[url(...)] bg-cover') // Scale to cover (may crop)
WDiv(className: 'bg-[url(...)] bg-contain') // Scale to fit (may have gaps)
```
### Position
Control the position of the background image using `bg-{side}` utilities.
```dart
WDiv(className: 'bg-[url(...)] bg-center') // Center
WDiv(className: 'bg-[url(...)] bg-top-left') // Top Left
WDiv(className: 'bg-[url(...)] bg-bottom') // Bottom Center
```
Supported values: `top`, `bottom`, `left`, `right`, `center`, `top-left`, `top-right`, `bottom-left`, `bottom-right`.
### Repeat
Control how the background image repeats.
```dart
WDiv(className: 'bg-[url(...)] bg-no-repeat') // Default
WDiv(className: 'bg-[url(...)] bg-repeat') // Tile horizontally and vertically
WDiv(className: 'bg-[url(...)] bg-repeat-x') // Tile horizontally
WDiv(className: 'bg-[url(...)] bg-repeat-y') // Tile vertically
```
## Responsive Design
Prefix any background utility with a breakpoint modifier to apply it at specific screen sizes.
```dart
WDiv(
// Mobile: Cover
// Desktop: Contain
className: 'bg-[url(...)] bg-cover lg:bg-contain'
)
```
## Dark Mode
Use the `dark:` prefix to change the background image in dark mode.
```dart
WDiv(
className: 'bg-[url(bg-light.png)] dark:bg-[url(bg-dark.png)]'
)
```
## Arbitrary Values
The `bg-[...]` syntax allows you to pass any string value.
```dart
// Standard URL syntax
WDiv(className: 'bg-[url(https://site.com/img.png)]')
// Direct path syntax (also supported)
WDiv(className: 'bg-[/path/to/local/file.png]')
```
## Customizing Theme
Background images are typically dynamic and not defined in the theme. However, the parser handles `bg-*` classes for colors as well. If you need to customize background **colors**, see the [Background Color](./background-color.md) documentation.
For images, Wind uses standard Flutter `ImageProvider` implementations based on the URL format:
- URLs starting with `http`/`https` become `NetworkImage`
- URLs starting with `/` become `FileImage`
- All others become `AssetImage` (prefixed with `assets/` unless using `~/` or `@/`)
## Related Documentation
- [Background Color](./background-color.md)
- [Background Gradient](./background-gradient.md)
---
# Shadow
# Shadow
Utilities for controlling the box shadow of an element.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Basic examples
WDiv(className: 'shadow')
WDiv(className: 'shadow-lg')
WDiv(className: 'shadow-red-500')
WDiv(className: 'shadow-none')
```
## Basic Usage
Use `shadow-{size}` utilities to apply different shadow intensities to an element.
```dart
WDiv(
className: 'shadow-lg bg-white rounded-lg p-6',
child: WText('Shadow Example'),
)
```
## Quick Reference
| Class | Properties | Description |
|:------|:-----------|:------------|
| `shadow-sm` | `blur: 2` | Subtle shadow |
| `shadow` | `blur: 3` | Default shadow |
| `shadow-md` | `blur: 6` | Medium shadow |
| `shadow-lg` | `blur: 15` | Large shadow |
| `shadow-xl` | `blur: 25` | Extra large shadow |
| `shadow-2xl` | `blur: 50` | Dramatic shadow |
| `shadow-none` | `none` | Removes shadow |
## Variants
### Shadow Colors
Use `shadow-{color}` utilities to change the color of the shadow. This preserves the original shadow's opacity structure while tinting it with the specified color.
```dart
// Blue tinted shadow
WDiv(className: 'shadow-lg shadow-blue-500')
// Red tinted shadow
WDiv(className: 'shadow-md shadow-red-500')
```
| Class | Description |
|:------|:------------|
| `shadow-{color}` | Tints the shadow with the specified color (e.g., `shadow-red-500`) |
### Shadow Opacity
Control shadow color opacity with the `/` modifier.
```dart
WDiv(className: 'shadow-xl shadow-red-500/50')
```
### Removing Shadows
Use `shadow-none` to remove any existing box shadows from an element. This is useful for resetting shadows at specific breakpoints.
```dart
WDiv(className: 'shadow-lg md:shadow-none')
```
## Responsive Design
Apply different shadow utilities at different breakpoints using the standard `sm:`, `md:`, `lg:`, `xl:`, and `2xl:` prefixes.
```dart
WDiv(className: 'shadow-sm md:shadow-lg xl:shadow-2xl')
```
## Dark Mode
Use the `dark:` prefix to apply different shadow styles when the application is in dark mode.
```dart
WDiv(className: 'shadow-lg bg-white dark:bg-gray-800 dark:shadow-black/30')
```
## Arbitrary Values
If the built-in scale doesn't meet your needs, use bracket notation to apply custom shadow colors.
```dart
// Custom shadow color hex
WDiv(className: 'shadow-lg shadow-[#50d71e]')
```
> [!NOTE]
> Currently, arbitrary values only support custom colors. For custom shadow sizes (offsets, blur), use the theme customization options.
## Customizing Theme
To extend or override the default shadow scale, modify the `shadows` property in `WindThemeData`.
```dart
WindThemeData(
shadows: {
// Add a custom shadow preset
'soft': [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: Offset(0, 4),
),
],
// Override the default 'xl' shadow
'xl': [
BoxShadow(
color: Colors.blue.withOpacity(0.2),
blurRadius: 30,
spreadRadius: 5,
),
],
},
)
```
Usage:
```dart
WDiv(className: 'shadow-soft')
```
## Related Documentation
- [Opacity](./opacity.md)
- [Ring](../borders/ring.md)
- [Background Color](../styling/background-color.md)
---
# Opacity
# Opacity
Utilities for controlling the opacity of an element.
## Table of Contents
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WDiv(className: 'opacity-100 bg-blue-500')
WDiv(className: 'opacity-75 bg-blue-500')
WDiv(className: 'opacity-50 bg-blue-500')
WDiv(className: 'opacity-25 bg-blue-500')
WDiv(className: 'opacity-0 bg-blue-500')
```
## Basic Usage
Control the opacity of an element using `opacity-{value}` utilities. This affects the entire element, including its content, background, and borders.
```dart
WDiv(
className: 'opacity-50 bg-blue-500 text-white p-4',
child: WText('This entire element is 50% opaque'),
)
```
> [!NOTE]
> This utility sets the Flutter `Opacity` widget or `color.withOpacity()` equivalent depending on context. To change only the background color's opacity without affecting text, use the color opacity modifier (e.g., `bg-blue-500/50`) instead.
## Quick Reference
| Class | Value | Description |
|:--- |:--- |:--- |
| `opacity-0` | 0 | Fully transparent |
| `opacity-5` | 0.05 | |
| `opacity-10` | 0.1 | |
| `opacity-20` | 0.2 | |
| `opacity-25` | 0.25 | |
| `opacity-30` | 0.3 | |
| `opacity-40` | 0.4 | |
| `opacity-50` | 0.5 | |
| `opacity-60` | 0.6 | |
| `opacity-70` | 0.7 | |
| `opacity-75` | 0.75 | |
| `opacity-80` | 0.8 | |
| `opacity-90` | 0.9 | |
| `opacity-95` | 0.95 | |
| `opacity-100` | 1 | Fully opaque |
## Variants
Wind supports all standard state variants for opacity.
```dart
// Change opacity on hover
WDiv(className: 'opacity-50 hover:opacity-100')
// Change opacity when button is disabled
WButton(className: 'bg-blue-500 disabled:opacity-50')
// Change opacity on focus
WInput(className: 'opacity-75 focus:opacity-100')
```
## Responsive Design
Change opacity based on screen size using responsive prefixes.
```dart
WDiv(className: 'opacity-100 md:opacity-50 lg:opacity-25')
```
## Dark Mode
Adjust opacity based on the current theme brightness.
```dart
WDiv(className: 'opacity-100 dark:opacity-80')
```
## Arbitrary Values
Use square brackets to define a custom opacity value between `0.0` and `1.0`.
```dart
WDiv(className: 'opacity-[0.67]')
WDiv(className: 'opacity-[0.125]')
```
> [!WARNING]
> Values are clamped between 0.0 and 1.0. `opacity-[1.5]` will resolve to `1.0`.
## Customizing Theme
You can customize the available opacity scale in your `WindThemeData`.
```dart
WindTheme(
data: WindThemeData(
opacities: {
'disabled': 0.35,
'faint': 0.10,
'glass': 0.85,
},
),
child: MyApp(),
)
```
Now you can use these custom keys in your utilities:
```dart
WDiv(className: 'opacity-disabled') // 0.35
WDiv(className: 'opacity-glass') // 0.85
```
## Related Documentation
- [Background Color](/doc/styling/background-color.md) - Using `/alpha` modifiers for backgrounds
- [Text Color](/doc/typography/text-color.md) - Using `/alpha` modifiers for text
- [Shadow](/doc/styling/shadow.md) - Box shadow utilities
---
# Border Width, Color, & Radius
# Border Width, Color, & Radius
Utilities for controlling border width, color, style, and border radius.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
## Basic Usage
```dart
// Basic border (1px solid gray-200 by default)
WDiv(className: 'border')
// Thicker border with color
WDiv(className: 'border-2 border-blue-500')
// Rounded corners
WDiv(className: 'border rounded-lg p-4')
// Specific side border
WDiv(className: 'border-b-2 border-red-500')
```
## Quick Reference
### Border Width
| Class | CSS Equivalent | Description |
|:--- |:--- |:--- |
| `border-0` | `border-width: 0px` | No border |
| `border` | `border-width: 1px` | 1px border |
| `border-2` | `border-width: 2px` | 2px border |
| `border-4` | `border-width: 4px` | 4px border |
| `border-8` | `border-width: 8px` | 8px border |
### Border Style
| Class | CSS Equivalent | Description |
|:--- |:--- |:--- |
| `border-solid` | `border-style: solid` | Solid border (default) |
| `border-none` | `border-style: none` | No border |
### Border Radius
| Class | Value | Description |
|:--- |:--- |:--- |
| `rounded-none` | `0px` | No rounding |
| `rounded-sm` | `2px` | Small radius |
| `rounded` | `4px` | Default radius |
| `rounded-md` | `6px` | Medium radius |
| `rounded-lg` | `8px` | Large radius |
| `rounded-xl` | `12px` | Extra large radius |
| `rounded-2xl` | `16px` | 2x Extra large |
| `rounded-3xl` | `24px` | 3x Extra large |
| `rounded-full` | `9999px` | Fully rounded (pill/circle) |
## Variants
### Individual Sides
Control border width for specific sides using `-t`, `-r`, `-b`, and `-l` suffixes.
```dart
// Top border only
WDiv(className: 'border-t-4 border-indigo-500')
// Right and Left borders
WDiv(className: 'border-r border-l border-gray-300')
```
### Corner Radius
Control radius for specific corners or sides.
```dart
// Top corners only
WDiv(className: 'rounded-t-lg bg-white')
// Top-left corner only
WDiv(className: 'rounded-tl-md bg-blue-100')
```
| Class | Sides/Corner |
|:--- |:--- |
| `rounded-t-*` | Top-left & Top-right |
| `rounded-r-*` | Top-right & Bottom-right |
| `rounded-b-*` | Bottom-left & Bottom-right |
| `rounded-l-*` | Top-left & Bottom-left |
| `rounded-tl-*` | Top-left only |
| `rounded-tr-*` | Top-right only |
| `rounded-br-*` | Bottom-right only |
| `rounded-bl-*` | Bottom-left only |
### Color Opacity
You can control the opacity of border colors using the color opacity modifier.
```dart
// 50% opacity red border
WDiv(className: 'border-2 border-red-500/50')
```
## Responsive Design
Prefix any border utility with a breakpoint variant to apply it at specific screen sizes.
```dart
// No border on mobile, 2px border on tablet+
WDiv(className: 'border-0 md:border-2')
// Square on mobile, rounded on desktop
WDiv(className: 'rounded-none lg:rounded-xl')
```
## Dark Mode
Use the `dark:` prefix to style borders differently in dark mode.
```dart
WDiv(
className: 'border border-gray-200 dark:border-gray-700'
)
```
## Arbitrary Values
If you need a value that doesn't fit the theme, use square bracket notation.
```dart
// Specific pixel width
WDiv(className: 'border-[3px]')
// Specific hex color
WDiv(className: 'border-[#1da1f2]')
// Specific radius
WDiv(className: 'rounded-[10px]')
```
## Customizing Theme
Customize standard values in `WindThemeData`.
```dart
WindThemeData(
// Custom border widths
borderWidths: {
'DEFAULT': 1.0,
'3': 3.0, // adds border-3
'thin': 0.5, // adds border-thin
},
// Custom border radius
borderRadius: {
'DEFAULT': 4.0,
'xl': 12.0,
'mega': 32.0, // adds rounded-mega
},
// Custom colors
colors: {
'brand': Colors.indigo, // adds border-brand
},
)
```
## Related Documentation
- [Ring Utilities](/doc/borders/ring.md) - Focus rings and outlines
---
# Ring Width, Color, and Offset
# Ring Width, Color, and Offset
Utilities for creating outline rings with box-shadows. These are useful for focus states or highlighting elements without affecting layout.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Default ring (3px blue-500)
WDiv(className: 'ring')
// Custom width and color
WDiv(className: 'ring-4 ring-red-500')
// With offset
WDiv(className: 'ring-2 ring-offset-2')
```
## Basic Usage
Use `ring-{width}` utilities to apply a solid box-shadow to an element. By default, this adds a 3px blue ring.
> [!NOTE]
> Rings in Wind are implemented using `BoxShadow` with a spread radius and zero blur. They do not take up space in the layout, unlike borders.
```dart
WDiv(
className: 'ring-4 ring-blue-500 rounded-lg p-4',
child: WText('Ring Example'),
)
```
## Quick Reference
| Class | Value | Description |
|:------|:------|:------------|
| `ring-0` | 0px | No ring |
| `ring-1` | 1px | 1px ring width |
| `ring-2` | 2px | 2px ring width |
| `ring` | 3px | Default ring width (3px) |
| `ring-4` | 4px | 4px ring width |
| `ring-8` | 8px | 8px ring width |
| `ring-inset` | - | Forces ring to be on the inside |
| `ring-offset-{width}` | - | Simulates an offset by adding a transparent gap |
## Variants
### Ring Color
Use `ring-{color}` to set the color of the ring. You can also use opacity modifiers like `/50`.
```dart
WDiv(className: 'ring-2 ring-blue-500')
WDiv(className: 'ring-2 ring-red-500/50') // 50% opacity
```
### Ring Offset
Use `ring-offset-{width}` to simulate space between the element and the ring. This is helpful for focus states on colored backgrounds.
```dart
// Creates a 2px gap between the element and the ring
WDiv(className: 'ring-2 ring-offset-2 ring-offset-white')
```
| Class | Value | Description |
|:------|:------|:------------|
| `ring-offset-0` | 0px | No offset |
| `ring-offset-1` | 1px | 1px offset |
| `ring-offset-2` | 2px | 2px offset |
| `ring-offset-4` | 4px | 4px offset |
| `ring-offset-8` | 8px | 8px offset |
### Ring Inset
Use `ring-inset` to force the ring to render on the inside of the element instead of the outside. This is useful for elements that shouldn't overflow their container.
```dart
WDiv(className: 'ring-2 ring-inset ring-pink-500')
```
## Responsive Design
Apply different ring utilities at different breakpoints using the standard `sm:`, `md:`, `lg:`, `xl:`, and `2xl:` prefixes.
```dart
WDiv(className: 'ring-2 md:ring-4 lg:ring-8')
```
## Dark Mode
Use the `dark:` prefix to apply different ring styles when the application is in dark mode.
```dart
WDiv(className: 'ring-slate-900/5 dark:ring-white/10')
```
## Arbitrary Values
If the built-in scale doesn't meet your needs, use bracket notation to apply custom values directly.
```dart
// Custom color
WDiv(className: 'ring-[#50d71e]')
// Custom color with opacity
WDiv(className: 'ring-[#50d71e]/50')
```
> [!NOTE]
> Currently, arbitrary values are supported for **colors only** (`ring-[#hex]`). Arbitrary widths (`ring-[10px]`) are not yet supported in the parser.
## Customizing Theme
To extend or override the default scale for rings, modify the `WindThemeData` in your app root.
```dart
WindTheme(
data: WindThemeData(
// Default color when no ring color class is present
ringColor: Colors.blue,
// Custom ring widths
ringWidths: {
'DEFAULT': 3,
'6': 6,
'10': 10,
},
// Custom offset widths
ringOffsets: {
'3': 3,
'6': 6,
},
),
child: MyApp(),
)
```
## Related Documentation
- [Borders](./borders.md)
- [Box Shadow](../styling/shadow.md)
---
# Animation
# Animation
Utilities for animating elements with CSS-like animation classes. Whether you're building a loading state or adding some life to your UI, Wind's animation utilities make it easy to drop in common motion patterns.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Spin](#spin)
- [Ping](#ping)
- [Pulse](#pulse)
- [Bounce](#bounce)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
// Spinning loader
WDiv(className: 'animate-spin w-8 h-8 border-4 border-blue-500 rounded-full')
// Pulsing skeleton
WDiv(className: 'animate-pulse w-full h-4 bg-gray-200 rounded')
```
## Basic Usage
Use the `animate-{type}` classes to add pre-defined animations to any widget. These are great for loading indicators, notification badges, or grabbing a user's attention.
```dart
WIcon(
Icons.refresh,
className: 'animate-spin text-blue-600',
)
```
## Quick Reference
| Class | Value | Description |
|:------|:------|:------------|
| `animate-none` | none | Removes any active animation |
| `animate-spin` | spin | Continuous 360-degree rotation |
| `animate-ping` | ping | Scaling outward like a radar ping |
| `animate-pulse` | pulse | Gentle opacity fade in/out |
| `animate-bounce` | bounce | Vertical bouncing motion |
## Variants
### Spin
Use `animate-spin` for things like loading indicators. It provides a smooth, linear rotation. Let's look at a basic spinner:
```dart
WDiv(className: 'animate-spin w-6 h-6 border-2 border-t-transparent border-blue-500 rounded-full')
```
### Ping
The `animate-ping` utility makes an element scale and fade out, resembling a radar ping or notification alert. This is perfect for status indicators.
```dart
WDiv(className: 'relative flex h-3 w-3', children: [
WDiv(className: 'animate-ping absolute inline-flex h-full w-full rounded-full bg-sky-400 opacity-75'),
WDiv(className: 'relative inline-flex rounded-full h-3 w-3 bg-sky-500'),
])
```
### Pulse
Use `animate-pulse` to create a skeleton loading effect. It gently fades the opacity of the element, making it ideal for content that is still loading.
```dart
WDiv(className: 'animate-pulse flex space-x-4', children: [
WDiv(className: 'rounded-full bg-slate-200 h-10 w-10'),
WDiv(className: 'flex-1 space-y-6 py-1', children: [
WDiv(className: 'h-2 bg-slate-200 rounded'),
WDiv(className: 'grid grid-cols-3 gap-4', children: [
WDiv(className: 'h-2 bg-slate-200 rounded col-span-2'),
WDiv(className: 'h-2 bg-slate-200 rounded col-span-1'),
]),
]),
])
```
### Bounce
The `animate-bounce` utility is perfect for scroll indicators or call-to-action buttons that need a bit of personality.
```dart
WIcon(Icons.keyboard_arrow_down, className: 'animate-bounce text-slate-400')
```
## Responsive Design
You can enable or disable animations at specific breakpoints. For example, you might want to only animate an element on larger screens to keep the mobile experience "quiet".
```dart
WDiv(className: 'animate-none md:animate-spin')
```
## Dark Mode
Animations work seamlessly with dark mode. You'll typically just change the colors of the animated element when switching to dark mode.
```dart
WDiv(className: 'animate-pulse bg-gray-200 dark:bg-gray-700')
```
## Arbitrary Values
If the built-in animations don't quite fit, you can use arbitrary values to specify custom animation strings.
```dart
WDiv(className: 'animate-[wiggle_1s_ease-in-out_infinite]')
```
## Customizing Theme
Want to add your own animations to the system? You can extend the `animations` map in your `WindThemeData`.
```dart
WindThemeData(
animations: {
'wiggle': WindAnimationType.bounce, // Map to existing logic or custom type
},
)
```
## Related Documentation
- [Transitions](./transition.md) - Smooth property changes
- [Hover States](../core-concepts/state-management.md) - Interactive triggers
---
# Transitions
# Transitions
Utilities for controlling the duration and easing of state transitions in Wind.
- [Basic Usage](#basic-usage)
- [Quick Reference](#quick-reference)
- [Variants](#variants)
- [Duration](#duration)
- [Easing](#easing)
- [Responsive Design](#responsive-design)
- [Dark Mode](#dark-mode)
- [Arbitrary Values](#arbitrary-values)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
## Basic Usage
Transitions allow you to change property values smoothly over a given duration. In Wind, these are typically paired with state modifiers like `hover:`, `focus:`, or `active:`.
```dart
// Smoothly transition background color on hover
WDiv(
className: 'bg-blue-500 hover:bg-blue-700 duration-300 ease-in-out',
child: WText('Hover Me', className: 'text-white'),
)
```
By adding `duration-200`, the background color shift happens over 200 milliseconds instead of snapping instantly.
## Quick Reference
### Duration
| Class | Value | Description |
|:------|:------|:------------|
| `duration-75` | 75ms | Very fast transition |
| `duration-100` | 100ms | Fast transition |
| `duration-150` | 150ms | |
| `duration-200` | 200ms | Standard UI transition |
| `duration-300` | 300ms | |
| `duration-500` | 500ms | Noticeable transition |
| `duration-700` | 700ms | |
| `duration-1000` | 1000ms | Slow transition |
### Easing (Timing Function)
| Class | Value | Description |
|:------|:------|:------------|
| `ease-linear` | `Curves.linear` | Constant speed |
| `ease-in` | `Curves.easeIn` | Starts slow, ends fast |
| `ease-out` | `Curves.easeOut` | Starts fast, ends slow |
| `ease-in-out` | `Curves.easeInOut` | Slow start and end |
## Variants
### Duration
Use the `duration-{ms}` utility to set the length of the transition animation.
```dart
WDiv(className: 'duration-500 ...')
```
### Easing
Control the rate of change with `ease-{curve}` utilities. This defines the "feel" of the animation.
```dart
WDiv(className: 'ease-out duration-300 ...')
```
## Responsive Design
Apply different transition speeds or curves at different breakpoints using the standard responsive prefixes.
```dart
// Slow transition on desktop, fast on mobile
WDiv(className: 'duration-150 lg:duration-500')
```
## Dark Mode
Use the `dark:` prefix to apply different transition styles when the application is in dark mode.
```dart
WDiv(className: 'duration-200 dark:duration-500')
```
## Arbitrary Values
If the standard scale doesn't fit your specific animation needs, use bracket notation to provide a custom millisecond value.
```dart
WDiv(className: 'duration-[420ms] ease-[Curves.bounceOut]')
```
## Customizing Theme
To extend or override the default transition scales, modify `WindThemeData` in your app configuration.
```dart
WindTheme(
data: WindThemeData(
transitionDurations: {
'snappy': Duration(milliseconds: 50),
'lazy': Duration(milliseconds: 2000),
},
transitionCurves: {
'bounce': Curves.bounceOut,
},
),
child: MyApp(),
)
```
Usage: `duration-snappy`, `ease-bounce`.
## Related Documentation
- [Animation](./animation.md) - Explicit keyframe animations
- [Hover States](../core-concepts/state-management.md) - Handling interactions
- [Opacity](../styling/opacity.md) - Fade transitions
---
# Spacing Helpers
# Spacing Helpers
Wind provides programmatic access to your theme's spacing scale through helper functions and extensions. This allows you to maintain consistency when building custom widgets or manual layouts that aren't using `className` strings.
> [!NOTE]
> This page covers the **Dart API** for spacing. For CSS-like utility classes such as `p-4`, `m-2`, or `gap-6`, see the [Spacing Layout](../layout/spacing.md) documentation.
- [The wSpacing Function](#wspacing-function)
- [Context Extensions](#context-extensions)
- [Programmatic Access](#programmatic-access)
- [Theme Configuration](#theme-configuration)
## The wSpacing Function
The `wSpacing` helper is the primary way to calculate pixel values based on your theme's `baseSpacingUnit` (defaulting to 4px).
```dart
import 'package:fluttersdk_wind/fluttersdk_wind.dart';
// Calculates: multiplier * baseSpacingUnit
double padding = wSpacing(context, 4); // 16.0
double gap = wSpacing(context, 2.5); // 10.0
double tight = wSpacing(context, 0.5); // 2.0
```
### Signature
```dart
double wSpacing(BuildContext context, num multiplier)
```
| Parameter | Type | Description |
|:---|:---|:---|
| `context` | `BuildContext` | Required to look up the active `WindTheme`. |
| `multiplier` | `num` | The scale factor (e.g., 4 in `p-4`). |
## Context Extensions
For more ergonomic access, Wind adds the `wSpacingExt` shortcut to `BuildContext`.
```dart
Container(
padding: EdgeInsets.all(context.wSpacingExt(4)), // 16.0
child: MyWidget(),
)
```
## Programmatic Access
If you need to access the raw spacing unit or the entire theme configuration, you can use the `windThemeData` extension.
```dart
// Access the base unit directly
final unit = context.windThemeData.baseSpacingUnit; // 4.0
// Use it in complex calculations
double customCalc = (unit * 4) + 2.0;
```
## Theme Configuration
The spacing scale is controlled by the `baseSpacingUnit` property in your `WindThemeData`. Changing this value will globally update both your programmatic spacing and all CSS utility classes (like `p-4`).
```dart
WindTheme(
data: WindThemeData(
baseSpacingUnit: 5.0, // 1 unit = 5px
),
child: MyApp(),
)
```
With the above configuration:
- `wSpacing(context, 4)` returns **20.0**
- `WDiv(className: 'p-4')` applies **20.0px** padding.
Related Documentation:
- [Spacing Layout Utilities](../layout/spacing.md)
- [Theming Guide](../core-concepts/theming.md)
- [Context Extensions](./context-extensions.md)
---
# Typography Helpers
# Typography Helpers
- [Introduction](#introduction)
- [Global Functions](#global-functions)
- [wFontSize](#wfontsize)
- [wFontWeight](#wfontweight)
- [BuildContext Extensions](#buildcontext-extensions)
- [Theme Scale Access](#theme-scale-access)
- [Customizing Scales](#customizing-scales)
## Introduction
Wind provides a set of helper functions and extensions to access typography values directly from your Dart code. While utility classes are the primary way to style widgets, these helpers are essential when you need to calculate dimensions, pass styles to standard Material widgets, or build custom components that respect the design system.
## Global Functions
These functions provide a safe way to resolve typography values using the current `BuildContext`.
### wFontSize
The `wFontSize` function retrieves a pixel value from the theme's font size scale. It accepts the size name (e.g., 'sm', 'lg', '2xl') and returns the corresponding `double`.
```dart
double? size = wFontSize(context, 'lg'); // Returns 18.0 (default)
```
### wFontWeight
The `wFontWeight` function resolves a named font weight into a Flutter `FontWeight` object.
```dart
FontWeight? weight = wFontWeight(context, 'bold'); // Returns FontWeight.w700
```
Supported weight names include `thin`, `extralight`, `light`, `normal`, `medium`, `semibold`, `bold`, `extrabold`, and `black`.
## BuildContext Extensions
For a more ergonomic API, Wind extends `BuildContext` with shortcut methods. These are wrappers around the global functions and provide a cleaner syntax when working inside a `build` method.
```dart
// Font Size shortcut
final h1Size = context.wFontSizeExt('4xl');
// Font Weight shortcut
final strongWeight = context.wFontWeightExt('bold');
```
## Theme Scale Access
If you need to iterate over the entire scale or access specific metadata, you can retrieve the `WindThemeData` object directly from the context.
```dart
final theme = context.windThemeData;
// Access the raw scales
Map sizes = theme.fontSizes;
Map weights = theme.fontWeights;
Map letterSpacing = theme.tracking;
Map lineHeights = theme.leading;
Map families = theme.fontFamilies;
```
This is particularly useful when building dynamic UI elements like font pickers or settings menus:
```dart
// Example: Listing all available font sizes
return ListView(
children: context.windThemeData.fontSizes.keys.map((name) {
return ListTile(
title: Text('Size: $name'),
trailing: Text('${context.wFontSizeExt(name)}px'),
);
}).toList(),
);
```
## Customizing Scales
You can override any part of the typography system when initializing your `WindTheme`. This ensures that your custom values are available through all the helper functions described above.
```dart
WindTheme(
data: WindThemeData(
fontSizes: {
'display': 120.0,
'caption': 10.0,
},
fontWeights: {
'heavy': FontWeight.w900,
},
),
child: MyApp(),
)
```
Once defined, these custom keys are accessible just like the defaults:
```dart
final displaySize = context.wFontSizeExt('display'); // 120.0
```
---
# Color Helpers
# Color Helpers
- [Resolving Colors](#resolving-colors)
- [Hex Colors](#hex-colors)
- [Opacity Handling](#opacity-handling)
- [Material Color Inversion](#material-color-inversion)
- [Context Extensions](#context-extensions)
Wind provides a set of utility functions and extensions to help you resolve and manipulate colors programmatically, staying consistent with your theme.
## Resolving Colors
The `wColor` function is the primary way to resolve colors from the active `WindTheme`. It handles color names, shades, and even automatic dark mode overrides.
```dart
Color? wColor(
BuildContext context,
String colorName, {
int shade = 500,
String? darkColorName,
int darkShade = 500,
})
```
### Basic Usage
You can resolve a color by its name and shade:
```dart
// Resolves blue-500 from the theme
final primary = wColor(context, 'blue');
// Resolves red-600
final error = wColor(context, 'red', shade: 600);
// Resolves using color-shade string format
final danger = wColor(context, 'rose-500');
```
### Dark Mode Overrides
You can specify a different color or shade to be used when the theme is in dark mode:
```dart
final background = wColor(
context,
'slate-50',
darkColorName: 'slate-900',
);
```
## Hex Colors
The `hexToColor` function converts various hex string formats into Flutter `Color` objects.
```dart
Color hexToColor(String code)
```
### Supported Formats
| Format | Example | Result |
|:-------|:--------|:-------|
| 3-digit Hex | `#RGB` | `#RRGGBB` |
| 4-digit Hex | `#ARGB` | `#AARRGGBB` |
| 6-digit Hex | `#RRGGBB` | `0xFFRRGGBB` |
| 8-digit Hex | `#AARRGGBB` | `0xAARRGGBB` |
> [!NOTE]
> The hash (`#`) prefix is optional. `hexToColor('FF0000')` works identically to `hexToColor('#FF0000')`.
## Opacity Handling
Wind includes utilities for parsing Tailwind-style opacity modifiers and applying them to colors.
### Parsing Opacity
The `parseColorOpacity` function splits a class string into the color part and its opacity factor.
```dart
// Returns (colorPart: 'blue-500', opacity: 0.5)
final result = parseColorOpacity('blue-500/50');
// Supports arbitrary values: (colorPart: 'red-500', opacity: 0.75)
final arbitrary = parseColorOpacity('red-500/[75]');
```
### Applying Opacity
Use `applyOpacity` to programmatically adjust the alpha channel of any `Color`.
```dart
Color applyOpacity(Color color, double opacity)
```
```dart
final semiTransparent = applyOpacity(Colors.blue, 0.5);
```
## Material Color Inversion
The `invertMaterialColor` utility is used by the theme engine to automatically generate dark mode variants by swapping shades (e.g., 50 ↔ 900, 100 ↔ 800).
```dart
MaterialColor invertMaterialColor(MaterialColor color)
```
## Context Extensions
For more ergonomic access, Wind extends `BuildContext` with several color-related shortcuts.
### `context.windColors`
Returns the full color map from the current `WindThemeData`.
```dart
final redPalette = context.windColors['red'];
```
### `context.wColorExt`
A shortcut for calling `wColor(context, ...)`.
```dart
final primary = context.wColorExt('blue', shade: 600);
```
### `context.windIsDark`
Returns a boolean indicating if the current theme brightness is `Brightness.dark`.
```dart
if (context.windIsDark) {
// Do something specific for dark mode
}
```
---
# Responsive Helpers
# Responsive Helpers
Responsive helpers provide programmatic access to Wind's breakpoint system, platform detection, and screen metrics directly from your Dart code.
- [Context Extensions](#context-extensions)
- [Helper Functions](#helper-functions)
- [Breakpoint Reference](#breakpoint-reference)
- [Platform Detection](#platform-detection)
## Context Extensions
Wind extends `BuildContext` with ergonomic getters to check the current screen state. These are the most common way to handle conditional logic in your `build` methods.
### Breakpoint Shortcuts
| Extension | Type | Description |
|:----------|:-----|:------------|
| `context.wIsMobile` | `bool` | Returns `true` if screen is smaller than `md` |
| `context.wIsTablet` | `bool` | Returns `true` if screen is between `md` and `lg` |
| `context.wIsDesktop` | `bool` | Returns `true` if screen is at least `lg` |
| `context.wActiveBreakpoint` | `String` | Returns current breakpoint name (`sm`, `md`, etc.) |
Let's look at an example:
```dart
@override
Widget build(BuildContext context) {
// Use extensions for conditional layouts
if (context.wIsMobile) {
return MobileView();
}
return DesktopView();
}
```
### Manual Checks
If you need to check against a specific breakpoint name:
```dart
if (context.wScreenIsExt('xl')) {
// Screen is extra large or larger
}
```
## Helper Functions
While extensions are preferred for brevity, these global functions provide the underlying logic and can be used in any part of your widget tree.
### `wScreenIs(context, name)`
Returns `true` if the current screen width is **at least** the pixel value defined for the given breakpoint.
```dart
if (wScreenIs(context, 'md')) {
print('Tablet or larger');
}
```
### `wScreenCurrent(context)`
Returns the name of the currently active breakpoint based on the screen width.
```dart
String current = wScreenCurrent(context); // 'base', 'sm', 'md', etc.
```
### `wScreen(context, name)`
Returns the raw pixel value (`int`) for a specific breakpoint name from your theme.
```dart
int? mdValue = wScreen(context, 'md'); // 768
```
## Breakpoint Reference
By default, Wind uses the standard Tailwind CSS breakpoint scale. You can override these in your `WindThemeData`.
| Name | Value | Range |
|:-----|:------|:------|
| `base` | `0px` | `< 640px` |
| `sm` | `640px` | `>= 640px` |
| `md` | `768px` | `>= 768px` |
| `lg` | `1024px` | `>= 1024px` |
| `xl` | `1280px` | `>= 1280px` |
| `2xl` | `1536px` | `>= 1536px` |
> [!NOTE]
> Breakpoint checks are inclusive (mobile-first). `wScreenIs(context, 'md')` is true for any width >= 768px, including `lg` and `xl` sizes.
## Platform Detection
Wind provides platform-specific modifiers for utility classes (e.g., `ios:p-4`), but you can also access platform information programmatically via `WindContext`.
```dart
final wind = WindContext.build(context);
if (wind.isMobile) {
// Physical mobile device (iOS/Android)
}
print(wind.platform); // 'ios', 'android', 'web', 'macos', etc.
```
For more details on responsive styling using class names, see the [Responsive Design](/doc/core-concepts/responsive-design) guide.
---
# Context Extensions
# Context Extensions
- [Overview](#overview)
- [Theme Extensions](#theme-extensions)
- [Responsive Extensions](#responsive-extensions)
- [Helper Extensions](#helper-extensions)
- [API Reference](#api-reference)
- [Common Patterns](#common-patterns)
## Overview
Wind provides a set of ergonomic extensions on `BuildContext` to make accessing theme data, responsive breakpoints, and styling helpers as concise as possible. Instead of verbose provider lookups, you can access the entire Wind ecosystem directly from the `context`.
## Theme Extensions
These extensions provide direct access to the `WindTheme` configuration and its underlying data.
### Theme Control
Use `windTheme` to interact with the theme controller, typically for toggling between light and dark modes.
```dart
// Toggle the current theme
context.windTheme.toggleTheme();
// Set a specific brightness
context.windTheme.setBrightness(Brightness.dark);
```
### Theme Data
Access the raw `WindThemeData` or specific properties like colors and brightness.
```dart
// Access the full theme data object
final theme = context.windThemeData;
// Quick access to the colors map
final primary = context.windColors['blue'];
// Check current brightness state
if (context.windIsDark) {
// Apply dark mode logic
}
```
## Responsive Extensions
Responsive extensions allow you to make layout decisions in your widget tree based on the current screen size and defined breakpoints.
### Breakpoint Shortcuts
Quickly determine the current device category. These are based on your theme's `screens` configuration (defaults to Tailwind breakpoints).
```dart
if (context.wIsMobile) {
return MobileLayout(); // < md
}
if (context.wIsTablet) {
return TabletLayout(); // >= md && < lg
}
if (context.wIsDesktop) {
return DesktopLayout(); // >= lg
}
```
### Active Breakpoint
You can also retrieve the exact name of the active breakpoint.
```dart
WText('Current breakpoint: ${context.wActiveBreakpoint}');
```
## Helper Extensions
These extensions are shorthand for Wind's global helper functions, allowing you to resolve specific values from the theme without passing the context manually.
### Colors and Spacing
Resolve colors and spacing values based on your theme's unit scale.
```dart
// Get 'red-500' from the theme
final alertColor = context.wColorExt('red', shade: 500);
// Get 4 units of spacing (4 * baseSpacingUnit)
final padding = context.wSpacingExt(4);
```
### Typography and Styling
Directly resolve font properties or even parse full class strings into a `WindStyle` object.
```dart
final weight = context.wFontWeightExt('bold');
final size = context.wFontSizeExt('lg');
// Parse a class string manually
final style = context.wStyleExt('p-4 bg-blue-500 rounded-lg');
```
## API Reference
| Property / Method | Returns | Description |
|:---|:---|:---|
| `windTheme` | `WindThemeController` | Access the controller for toggling or updating the theme. |
| `windThemeData` | `WindThemeData` | Access the read-only theme configuration. |
| `windColors` | `Map` | Returns the full color palette from the theme. |
| `windScreens` | `Map` | Returns the breakpoint map (e.g., `md: 768`). |
| `windBrightness` | `Brightness` | Returns the current theme brightness (`light` or `dark`). |
| `windIsDark` | `bool` | Convenience getter for `brightness == Brightness.dark`. |
| `wActiveBreakpoint` | `String` | Returns the name of the currently active breakpoint. |
| `wIsMobile` | `bool` | Returns `true` if screen width is less than `md`. |
| `wIsTablet` | `bool` | Returns `true` if screen width is between `md` and `lg`. |
| `wIsDesktop` | `bool` | Returns `true` if screen width is `lg` or larger. |
| `wColorExt(name, {shade})`| `Color?` | Resolves a theme color by name and optional shade. |
| `wSpacingExt(multiplier)` | `double` | Returns spacing calculated by `multiplier * baseSpacingUnit`. |
| `wFontSizeExt(name)` | `double?` | Returns the font size value for a key (e.g., `xl`). |
| `wFontWeightExt(name)` | `FontWeight?` | Returns the font weight for a key (e.g., `bold`). |
| `wScreenIsExt(name)` | `bool` | Checks if a specific breakpoint is currently active. |
| `wStyleExt(className)` | `WindStyle` | Parses a utility string into a `WindStyle` object. |
## Common Patterns
### Conditional Layouts
Using responsive extensions to change widget parameters.
```dart
WDiv(
className: 'bg-white rounded-lg',
// Use extension to adjust padding dynamically in logic
child: Padding(
padding: EdgeInsets.all(context.wIsMobile ? 12 : 24),
child: Content(),
),
)
```
### Manual Style Resolution
Sometimes you need to apply Wind styles to standard Flutter widgets that don't support `className`.
```dart
final style = context.wStyleExt('text-blue-600 font-bold italic');
return Text(
'Direct Styling',
style: style.textStyle,
);
```
### Theme Toggling
Implementing a theme switcher is trivial with the `windTheme` extension.
```dart
IconButton(
icon: Icon(context.windIsDark ? Icons.light_mode : Icons.dark_mode),
onPressed: () => context.windTheme.toggleTheme(),
)
```
## Related Documentation
- [Theming Concepts](../core-concepts/theming.md)
- [Responsive Design](../core-concepts/responsive-design.md)
- [Color Helpers](./color-helpers.md)
---
# Style Parser
# Style Parser
- [Introduction](#introduction)
- [The wStyle Helper](#wstyle-helper)
- [WindParser Engine](#windparser-engine)
- [Performance & Caching](#performance-and-caching)
- [Context Extensions](#context-extensions)
## Introduction
While Wind widgets like `WDiv` and `WText` are the primary way to use utility classes, you may sometimes need to parse utility strings programmatically. The Style Parser API allows you to convert any `className` string into a structured `WindStyle` object that can be applied to standard Flutter widgets.
## The wStyle Helper
The `wStyle` function is a global helper that provides the simplest way to parse utility classes on the fly.
```dart
WindStyle wStyle(BuildContext context, String className)
```
### Usage Example
This is particularly useful when building custom widgets that need to accept Wind utilities for styling:
```dart
Widget buildCustomCard(BuildContext context, {String? className}) {
final style = wStyle(context, className ?? 'bg-white p-4 shadow-md');
return Container(
decoration: style.decoration,
padding: style.padding,
child: Text('Styled with WindStyle'),
);
}
```
## WindParser Engine
For more advanced scenarios, you can interface directly with `WindParser.parse`. This method allows for base style merging and manual state injection.
```dart
static WindStyle parse(
String className,
BuildContext context, {
WindStyle? baseStyle,
Set? states,
})
```
### Parameters
| Parameter | Type | Description |
|:---|:---|:---|
| `className` | `String` | The utility class string to parse. |
| `context` | `BuildContext` | Used to resolve theme values, breakpoints, and platform. |
| `baseStyle` | `WindStyle?` | An optional base style to merge the new utilities into. |
| `states` | `Set?` | Manual state overrides (e.g., `{'loading', 'active'}`). |
### Accessing Parsed Values
The resulting `WindStyle` object contains resolved Flutter properties:
```dart
final style = wStyle(context, 'bg-red-500 text-lg font-bold');
Color? color = style.decoration?.color; // Resolved bg-red-500
TextStyle? textStyle = style.textStyle; // Resolved text-lg and font-bold
EdgeInsets? padding = style.padding; // resolved p-* classes
```
## Performance & Caching
Wind uses an internal memoization system to ensure that parsing is extremely fast. The `WindParser` maintains a static cache keyed by a combination of:
1. The raw `className` string.
2. The current active breakpoint (`sm`, `md`, etc.).
3. The current brightness (Light/Dark mode).
4. The target platform (iOS/Android/Web).
5. The active interaction states (`hover`, `focus`, etc.).
> [!NOTE]
> Because of this caching, calling `wStyle` multiple times with the same parameters in a build method has negligible performance impact.
## Context Extensions
For cleaner syntax, Wind provides a BuildContext extension that mirrors the global helper:
```dart
// Using global helper
final style = wStyle(context, 'p-4');
// Using extension
final style = context.wStyleExt('p-4');
```
### Related Documentation
- [Context Extensions](./context-extensions.md)
- [Color Helpers](./color-helpers.md)
- [Theming Concepts](../core-concepts/theming.md)
---
# WText
# WText
A utility-first text component that translates Tailwind-like class strings into optimized Flutter typography.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Runtime-Dynamic Colors](#runtime-dynamic-colors)
- [Typography Styling](#typography-styling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WText(
'Design is not just what it looks like and feels like.',
className: 'text-2xl font-bold text-slate-900 text-center leading-tight',
)
```
## Basic Usage
The `WText` widget handles all text rendering in Wind. Unlike standard Flutter `Text` widgets, it supports direct styling via the `className` property, including responsive and state-based modifiers.
```dart
WText(
'Utility-first styling for Flutter',
className: 'text-lg text-blue-600 font-semibold italic p-4',
)
```
## Constructor
```dart
const WText(
String data, {
Key? key,
String? className,
WindStyle? style,
TextStyle? textStyle,
bool selectable = false,
Set? states,
Color? foregroundColor,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `data` | `String` | `required` | The text string to display. |
| `className` | `String?` | `null` | Tailwind-like utility classes. |
| `style` | `WindStyle?` | `null` | Explicit WindStyle object as a base. |
| `textStyle` | `TextStyle?` | `null` | Standard Flutter TextStyle to merge (className takes precedence). |
| `selectable` | `bool` | `false` | Whether the text should be selectable (renders `SelectableText`). |
| `states` | `Set?` | `null` | Custom states for dynamic styling (e.g., 'loading'). |
| `foregroundColor` | `Color?` | `null` | Inline text color for runtime-dynamic values. Overrides any `text-*` / `dark:text-*` from `className`. See [Runtime-Dynamic Colors](#runtime-dynamic-colors). |
## Runtime-Dynamic Colors
Utility classes are for **design tokens** (`text-primary-500`, `text-red-500`). When the color is a **runtime value**, a user-picked brand color or a hex loaded from an API, use the `foregroundColor` prop instead of interpolating into `text-[#$hex]`.
```dart
// Good: runtime-dynamic color via inline prop
WText(
'Brand',
foregroundColor: userBrandColor,
className: 'text-lg font-bold',
)
// Avoid: string interpolation bloats the parser cache, one entry per unique hex
WText('Brand', className: 'text-lg font-bold text-[#${userBrandColor.hex}]')
```
Precedence: inline `foregroundColor` wins over any `text-*` / `dark:text-*` resolved from `className`. When `foregroundColor` is `null`, className behavior, including the `dark:` fallback, is unchanged. The inline color does not participate in the parser cache key.
## Typography Styling
WText supports a wide range of typography utilities that map directly to Flutter's `TextStyle`.
### Alignment and Transform
```dart
WText('CENTERED UPPERCASE', className: 'text-center uppercase')
WText('capitalize me', className: 'capitalize')
```
### Overflow and Clamping
Handle long text gracefully using truncation or line clamping.
```dart
WText(
'A very long text that will be truncated with an ellipsis after two lines.',
className: 'line-clamp-2 text-gray-500',
)
```
## State Variants
Text colors and weights can react to states when provided via the `states` prop or when nested within state-aware widgets like `WButton`.
```dart
WText(
'Hover me',
className: 'text-gray-500 hover:text-blue-600 transition-colors',
)
```
## Styling Examples
### Gradient and Opacity
While standard text uses `text-{color}`, you can also apply opacity modifiers.
```dart
WText(
'Faded Text',
className: 'text-blue-500/50 font-medium',
)
```
### Composition Pipeline
WText uniquely supports layout utilities (padding, margin, alignment) by automatically wrapping the text in the necessary layout widgets.
```dart
WText(
'Alert Message',
className: 'bg-red-100 text-red-700 p-4 rounded-lg border border-red-200',
)
```
## All Supported Classes
| Category | Classes |
|:---------|:--------|
| **Font Size** | `text-xs`, `text-sm`, `text-base`, `text-lg`, `text-xl` ... `text-6xl` |
| **Font Weight** | `font-thin`, `font-light`, `font-normal`, `font-medium`, `font-bold` ... `font-black` |
| **Color** | `text-{color}`, `text-{color}/{opacity}`, `text-[rgb(...)]` |
| **Align** | `text-left`, `text-center`, `text-right`, `text-justify` |
| **Transform** | `uppercase`, `lowercase`, `capitalize`, `normal-case` |
| **Decoration** | `underline`, `line-through`, `no-underline` |
| **Spacing** | `leading-{size}` (Line Height), `tracking-{size}` (Letter Spacing) |
| **Overflow** | `truncate`, `text-ellipsis`, `line-clamp-{n}` |
## Customizing Theme
Override default typography scales in your `WindThemeData`.
```dart
WindThemeData(
fontSizes: {
'huge': 120.0,
},
fontWeights: {
'thick': FontWeight.w900,
},
)
```
## Related Documentation
- [WDiv](./w-div.md) - For complex layouts and containers.
- [Typography Settings](../typography/font-size.md) - Deep dive into font sizes and scales.
- [State Management](../core-concepts/state-management.md) - How `hover:` and `focus:` work.
---
# WDiv
# WDiv
The fundamental building block of Wind. `WDiv` acts as a multi-purpose container that replaces standard Flutter widgets like `Container`, `Row`, `Column`, `Flex`, `Grid`, `Wrap`, and `SingleChildScrollView`.
It embodies the "Intelligent Composition" philosophy, dynamically constructing the most efficient widget tree based on the provided utility classes. Instead of blindly wrapping content, `WDiv` inspects the `className` and selectively applies specialized widgets like `Padding`, `Align`, `Row`, or `GridView` only when necessary.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Runtime-Dynamic Colors](#runtime-dynamic-colors)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WDiv(
className: 'flex flex-col gap-4 p-6 bg-white rounded-xl shadow-lg',
children: [
WText('Hello Wind', className: 'text-2xl font-bold'),
WText('This is a styled container.', className: 'text-gray-600'),
],
)
```
## Basic Usage
Use `WDiv` with a single `child` for simple wrapping or with `children` when using layout modes like `flex` or `grid`.
```dart
// Simple container
WDiv(
className: 'p-4 bg-blue-500 rounded',
child: WText('Single Child', className: 'text-white'),
)
// Flex row
WDiv(
className: 'flex flex-row gap-2',
children: [
WDiv(className: 'w-10 h-10 bg-red-500'),
WDiv(className: 'w-10 h-10 bg-green-500'),
],
)
```
## Constructor
```dart
const WDiv({
Key? key,
String? className,
Widget? child,
List? children,
WindStyle? style,
Set? states,
bool scrollPrimary = false,
Color? backgroundColor,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `className` | `String?` | `null` | Utility class string defining layout and styles. |
| `child` | `Widget?` | `null` | Single child widget. Mutually exclusive with `children`. |
| `children` | `List?` | `null` | List of children widgets. Mutually exclusive with `child`. |
| `style` | `WindStyle?` | `null` | Explicit style object that serves as a base for `className`. |
| `states` | `Set?` | `null` | Custom states to activate prefix classes (e.g., `loading:bg-blue-500`). |
| `scrollPrimary` | `bool` | `false` | Whether this is the primary scroll view for iOS tap-to-top and desktop scrollbar integration. |
| `backgroundColor` | `Color?` | `null` | Inline background color for runtime-dynamic values. Overrides any `bg-*` / `dark:bg-*` from `className`. See [Runtime-Dynamic Colors](#runtime-dynamic-colors). |
## Runtime-Dynamic Colors
Utility classes are for **design tokens** (`bg-primary-500`, `bg-red-500`). When the color is a **runtime value**, a hex from a color picker or a brand color loaded per tenant, use the `backgroundColor` prop instead of interpolating into `bg-[#$hex]`.
```dart
// Good: runtime-dynamic color via inline prop
WDiv(
backgroundColor: userBrandColor, // Color from a picker or API
className: 'w-16 h-16 rounded-xl',
)
// Avoid: string interpolation bloats the parser cache, one entry per unique hex
WDiv(className: 'w-16 h-16 rounded-xl bg-[#${userBrandColor.hex}]')
```
Precedence: inline `backgroundColor` wins over any `bg-*` / `dark:bg-*` resolved from `className`. When `backgroundColor` is `null`, className behavior, including the `dark:` fallback, is unchanged. The inline color does not participate in the parser cache key.
## Layout Modes
`WDiv` dynamically switches its internal structure based on the `display` utility classes provided in `className`.
- **Block (Default)**: Standard vertical stack or single child wrapper. If `children` is used without `flex` or `grid`, it defaults to a `Column`.
- **Flex**: Enabled via `flex`. Supports `flex-row`, `flex-col`, `gap-*`, `items-*`, `justify-*`. It mimics CSS Flexbox behavior, including automatic `Flexible` wrapping for children in rows.
- **Grid**: Enabled via `grid`. Uses a combination of `Wrap` and `LayoutBuilder` to achieve Tailwind-like grid behavior (`grid-cols-*`) with intrinsic item heights.
- **Wrap**: Enabled via `wrap`. Elements wrap to the next line when space is insufficient, similar to `flex-wrap` in CSS.
- **Hidden**: Enabled via `hidden`. The widget short-circuits to `SizedBox.shrink()` to save resources.
## Event Handling
`WDiv` automatically becomes interactive when state-based prefixes like `hover:`, `focus:`, or `active:` are used in the `className`. Under the hood, it wraps the content in a `WAnchor` to detect gestures and focus.
For direct gesture support (taps, long presses) or to create semantic buttons, use [WAnchor](/docs/widgets/w-anchor) or [WButton](/docs/widgets/w-button).
## State Variants
`WDiv` supports all Wind state prefixes, allowing you to change styles based on interaction or environment.
```dart
WDiv(
className: 'bg-blue-500 hover:bg-blue-600 active:scale-95 duration-200',
child: WText('Interactive', className: 'text-white'),
)
```
Common prefixes:
- `hover:` - Applies when the mouse pointer is over the element.
- `focus:` - Applies when the element has keyboard focus.
- `disabled:` - Applies when the element or parent is disabled.
- `dark:` - Applies when the application is in dark mode.
- `active:` - Applies while the element is being pressed.
## Styling Examples
### Backgrounds & Borders
Combine background colors, gradients, and border properties.
```dart
WDiv(className: 'bg-red-500 border-2 border-red-700 rounded-lg')
```
### Shadows & Rings
Apply depth with shadows or focus indicators with rings.
```dart
WDiv(className: 'shadow-xl ring-2 ring-blue-500 ring-offset-2')
```
### Responsive Design
`WDiv` uses mobile-first breakpoints to adapt layouts to different screen sizes.
```dart
// Stacked on mobile, side-by-side on tablet/desktop
WDiv(className: 'flex flex-col md:flex-row gap-4')
// Variable widths
WDiv(className: 'w-full md:w-1/2 lg:w-1/3')
```
## All Supported Classes
| Category | Classes |
|:---------|:--------|
| **Display** | `block`, `flex`, `grid`, `wrap`, `hidden` |
| **Flexbox** | `flex-row`, `flex-col`, `justify-*`, `items-*`, `gap-*`, `flex-1`, `flex-2`, etc. |
| **Grid** | `grid-cols-*`, `gap-x-*`, `gap-y-*` |
| **Sizing** | `w-*`, `h-*`, `min-w-*`, `max-w-*`, `aspect-*` |
| **Spacing** | `p-*`, `px-*`, `py-*`, `m-*`, `mx-*`, `my-*`, `space-x-*`, `space-y-*` |
| **Background** | `bg-*`, `bg-gradient-*`, `bg-opacity-*` |
| **Borders** | `border-*`, `rounded-*`, `border-opacity-*` |
| **Effects** | `shadow-*`, `opacity-*`, `ring-*`, `ring-offset-*` |
| **Overflow** | `overflow-hidden`, `overflow-scroll`, `overflow-auto`, `overflow-x-*`, `overflow-y-*` |
| **Animation** | `animate-spin`, `animate-pulse`, `animate-bounce`, `animate-ping` |
| **Transitions** | `transition-*`, `duration-*`, `ease-*` |
## Customizing Theme
`WDiv` respects the configuration defined in `WindThemeData`.
```dart
WindTheme(
data: WindThemeData(
baseSpacingUnit: 4.0, // Multiplier for p-1, m-4, etc.
colors: { ... }, // Custom color palette for bg-*, border-*
breakpoints: { ... }, // Custom screen sizes for md:, lg:, etc.
),
child: ...,
)
```
## Related Documentation
- [Flexbox](../layout/flexbox.md) & [Grid](../layout/grid.md)
- [Sizing Utilities](../layout/sizing.md)
- [Spacing & Margins](../layout/spacing.md)
- [Interaction State Prefixes](../core-concepts/state-management.md)
- [WAnchor Widget](./w-anchor.md)
- [WSpacer Widget](./w-spacer.md)
---
# WSpacer
# WSpacer
The `WSpacer` is a lightweight widget designed specifically for adding consistent gaps between elements. Unlike `WDiv`, which supports a full range of decorations and layout properties, `WSpacer` renders as a simple `SizedBox`, making it highly efficient for layouts where you only need spacing.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Responsive Spacing](#responsive-spacing)
- [Why WSpacer?](#why-wspacer)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WDiv(
className: 'flex flex-col',
children: [
WText('Label', className: 'font-bold'),
WSpacer(className: 'h-2'), // 8px vertical gap
WInput(placeholder: 'Enter text...'),
],
)
```
## Basic Usage
The `WSpacer` widget extracts width and height values from your utility classes and applies them to a `SizedBox`. It supports the standard Wind spacing scale (based on a 4px unit by default).
### Vertical Spacing
Use the `h-{n}` classes to add vertical gaps in columns:
```dart
WSpacer(className: 'h-4') // 16px height
```
### Horizontal Spacing
Use the `w-{n}` classes to add horizontal gaps in rows:
```dart
WSpacer(className: 'w-2') // 8px width
```
## Constructor
```dart
const WSpacer({
Key? key,
String? className,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `className` | `String?` | `null` | Wind utility classes for sizing (h-*, w-*). |
> [!NOTE]
> Non-sizing classes (like `bg-red-500` or `p-4`) are parsed but ignored by `WSpacer` as it renders a `SizedBox` which does not support decoration or padding.
## Responsive Spacing
You can adjust spacing based on screen size using responsive prefixes. This is particularly useful for increasing margins on tablet or desktop layouts.
```dart
WSpacer(className: 'h-4 md:h-8')
```
## Why WSpacer?
While you could use `WDiv` with padding or margin, `WSpacer` is preferred for explicit gaps for several reasons:
1. **Performance:** It bypasses complex composition and renders as a single `SizedBox`.
2. **Semantics:** It clearly communicates "this widget exists only for spacing" to other developers.
3. **Convenience:** It replaces the verbose `SizedBox(height: 16)` with the more maintainable `WSpacer(className: 'h-4')`.
## All Supported Classes
| Category | Classes | Description |
|:---------|:--------|:------------|
| Sizing | `h-{n}`, `w-{n}` | Standard scale (h-1, h-2, etc.) |
| Arbitrary | `h-[15px]`, `w-[2px]` | Custom pixel values |
| Responsive | `md:h-*`, `lg:w-*` | Breakpoint-specific spacing |
## Customizing Theme
The pixel values for `h-{n}` and `w-{n}` are derived from the `baseSpacingUnit` in your theme.
```dart
WindThemeData(
baseSpacingUnit: 4.0, // Default: h-1 = 4px
)
```
## Related Documentation
- [WDiv](./w-div.md)
- [Sizing Utilities](../layout/sizing.md)
- [Spacing Utilities](../layout/spacing.md)
- [Responsive Design](../core-concepts/responsive-design.md)
---
# WIcon
# WIcon
The utility-first icon component for displaying vector icons with Tailwind-like styling. It wraps Flutter's `Icon` widget and applies utility classes for sizing, coloring, and animations while inheriting styles from the surrounding text context.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WIcon(
Icons.star,
className: 'text-yellow-400 text-3xl',
)
```
## Basic Usage
The `WIcon` widget allows you to style icons using `className`. By default, it inherits color and font size from the surrounding `DefaultTextStyle` (e.g., from a parent `WDiv` or `WText`), allowing icons to automatically match adjacent text.
```dart
WIcon(
Icons.favorite,
className: 'text-red-500 text-xl',
)
```
## Constructor
```dart
const WIcon(
IconData icon, {
Key? key,
String? className,
Set? states,
String? semanticLabel,
TextDirection? textDirection,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `icon` | `IconData` | **Required** | The icon to display. |
| `className` | `String?` | `null` | Wind utility classes for styling. |
| `states` | `Set?` | `null` | Custom states to activate prefix classes. |
| `semanticLabel` | `String?` | `null` | Optional semantic label for accessibility. |
| `textDirection` | `TextDirection?` | `null` | Text direction for the icon. |
## Layout Modes
While icons don't have complex layout modes, `WIcon` supports two primary sizing methods. Explicit sizing (`w-*` / `h-*`) takes precedence over typography-based sizing (`text-*`).
### Sizing Modes
```dart
// Typography-based sizing (inherits line-height context)
WIcon(Icons.info, className: 'text-lg')
// Explicit dimension sizing (absolute pixels)
WIcon(Icons.info, className: 'w-12 h-12')
```
## Event Handling
`WIcon` is a purely visual component. To handle user interactions, wrap it in a `WAnchor` or `WButton`. You can pass the parent's state to the `states` prop to trigger state-aware styling.
```dart
WAnchor(
onTap: () => print('Settings tapped'),
child: WIcon(
Icons.settings,
className: 'text-gray-400 hover:text-blue-500 transition-colors',
),
)
```
## State Variants
Use state prefixes for dynamic appearance changes. This works seamlessly when the icon is part of an interactive group.
```dart
WIcon(
Icons.check_circle,
className: 'text-green-500 dark:text-green-400 hover:scale-110',
)
```
## Styling Examples
### Colors and Opacity
Icons support full color palette integration including opacity modifiers.
```dart
// Red icon with 50% opacity
WIcon(Icons.error, className: 'text-red-500/50')
```
### Animations
Apply built-in animations to icons for loading states or attention-grabbing effects.
```dart
// Spinning refresh icon
WIcon(Icons.refresh, className: 'text-blue-600 animate-spin')
// Pulsing alert icon
WIcon(Icons.notifications, className: 'text-amber-500 animate-pulse')
```
## All Supported Classes
| Category | Classes |
|:---------|:--------|
| Sizing | `text-{size}`, `w-{size}`, `h-{size}` |
| Colors | `text-{color}`, `text-{color}/{opacity}` |
| Opacity | `opacity-{n}` |
| Animation | `animate-spin`, `animate-pulse`, `animate-bounce`, `animate-ping` |
| Transitions | `duration-{ms}`, `ease-{curve}` |
## Customizing Theme
Icons respect the global configuration in `WindThemeData`. Changing the `fontSizes` or `colors` scales will update all icons using those classes.
```dart
WindThemeData(
fontSizes: {
'xl': 20.0,
},
colors: {
'brand': Colors.indigo,
},
)
```
## Related Documentation
- [WText](./w-text.md) - Cascades typography styles to icons
- [WAnchor](./w-anchor.md) - For making icons interactive
- [WSvg](./w-svg.md) - For custom vector illustrations
- [WImage](./w-image.md) - For rasterized images
---
# WImage
# WImage
The `WImage` widget brings utility-first styling to images in Flutter, providing HTML-like semantics with Tailwind-inspired classes for sizing, object fitting, and decoration.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WImage(
src: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e',
alt: 'Forest landscape',
className: 'w-full h-64 object-cover rounded-xl shadow-lg',
)
```
## Basic Usage
The `WImage` widget handles both network and asset images. For network images, simply provide the URL to the `src` property. For asset images, use the `asset://` prefix.
```dart
// Network Image
WImage(
src: 'https://example.com/image.jpg',
className: 'w-32 h-32 rounded-full',
)
// Asset Image
WImage(
src: 'asset://assets/images/logo.png',
className: 'w-12 h-12',
)
```
## Constructor
```dart
const WImage({
Key? key,
String? src,
ImageProvider? image,
String? alt,
String? className,
Set? states,
Widget? placeholder,
ImageErrorBuilder? errorBuilder,
ImageLoadingBuilder? loadingBuilder,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `src` | `String?` | `null` | Image source URL. Prefix with `asset://` for local assets. |
| `image` | `ImageProvider?` | `null` | Direct ImageProvider (e.g., `NetworkImage`, `FileImage`). |
| `alt` | `String?` | `null` | Alternative text for accessibility (semantic label). |
| `className` | `String?` | `null` | Wind utility classes for sizing, fit, and decoration. |
| `states` | `Set?` | `null` | Custom states for dynamic styling. |
| `placeholder` | `Widget?` | `null` | Widget to show while the image is loading. |
| `errorBuilder` | `ImageErrorBuilder?` | `null` | Custom builder for error handling. |
| `loadingBuilder` | `ImageLoadingBuilder?` | `null` | Custom builder for loading indicators. |
> [!NOTE]
> Either `src` or `image` must be provided. If `src` is provided, it takes precedence in determining the `ImageProvider`.
## Layout Modes
`WImage` supports common image layout utilities to control how the image is resized and fitted within its container.
### Object Fit
Control how the image content should be resized to fit its container using `object-{fit}` classes.
```dart
WImage(
src: 'https://example.com/photo.jpg',
className: 'w-64 h-64 object-contain bg-gray-200',
)
```
### Aspect Ratio
Ensure the image maintains a specific aspect ratio regardless of its content dimensions.
```dart
WImage(
src: 'https://example.com/banner.jpg',
className: 'w-full aspect-video object-cover',
)
```
## Event Handling
`WImage` is a display-only widget and does not include built-in gesture handlers. To make an image interactive, wrap it in a `WAnchor` or `WButton`.
```dart
WAnchor(
onTap: () => print('Image clicked!'),
child: WImage(
src: 'https://example.com/avatar.jpg',
className: 'w-12 h-12 rounded-full hover:opacity-80 transition-opacity',
),
)
```
## State Variants
You can use state prefixes to change the image's appearance based on parent state or custom state sets.
```dart
WImage(
src: 'https://example.com/photo.jpg',
className: 'opacity-100 hover:opacity-75 transition-opacity duration-300',
)
```
## Styling Examples
### Profile Avatars
Creating circular avatars with borders and shadows.
```dart
WImage(
src: 'https://example.com/user.jpg',
className: 'w-16 h-16 rounded-full border-2 border-white shadow-sm object-cover',
)
```
### Rounded Cards
Using specific border radius and shadow depth.
```dart
WImage(
src: 'https://example.com/thumbnail.jpg',
className: 'w-full h-48 rounded-t-lg object-cover',
)
```
## All Supported Classes
| Category | Classes |
|:---------|:--------|
| Sizing | `w-{size}`, `h-{size}`, `max-w-{size}`, `min-h-{size}` |
| Object Fit | `object-cover`, `object-contain`, `object-fill`, `object-none`, `object-scale-down` |
| Aspect Ratio | `aspect-square`, `aspect-video`, `aspect-{ratio}` |
| Borders | `border`, `border-{width}`, `border-{color}`, `rounded-{size}` |
| Effects | `shadow-{size}`, `opacity-{n}` |
## Customizing Theme
The default spacing, border radius, and colors used by `WImage` are controlled via `WindThemeData`.
```dart
WindThemeData(
borderRadius: {
'xl': 12.0,
},
// WImage also respects the default spacing scale for w-* and h-* classes
)
```
## Related Documentation
- [WDiv](./w-div.md) - The fundamental layout container.
- [WSvg](./w-svg.md) - For vector-based graphics.
- [WIcon](./w-icon.md) - For system and custom icons.
---
# WSvg
# WSvg
The utility-first SVG component. `WSvg` brings HTML SVG semantics to Flutter with Tailwind-like utility classes for styling, supporting both asset paths and raw SVG strings.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [Preserving Original Colors](#preserving-original-colors)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WSvg(
src: 'assets/icons/star.svg',
className: 'fill-yellow-500 w-6 h-6 hover:scale-110 transition-transform',
)
```
## Basic Usage
The `WSvg` widget allows you to render SVG graphics and style them using utility classes. It inherits colors and sizes from parent styles if not explicitly defined.
### From Asset
Use the default constructor for files located in your assets folder.
```dart
WSvg(
src: 'assets/logo.svg',
className: 'w-12 h-12 fill-blue-600',
)
```
### From String
Use the `WSvg.string` constructor to render raw SVG content.
```dart
WSvg.string(
'',
className: 'stroke-red-500 stroke-2 w-8 h-8',
)
```
## Constructor
```dart
WSvg({
Key? key,
required String? src,
String? className,
Set? states,
String? semanticsLabel,
})
WSvg.string(
String svg, {
Key? key,
String? className,
Set? states,
String? semanticsLabel,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `className` | `String?` | `null` | Wind utility classes for sizing, coloring, and effects. |
| `src` | `String?` | `null` | The asset path to the SVG file. |
| `svgString` | `String?` | `null` | Raw SVG string content (used in `WSvg.string`). |
| `states` | `Set?` | `null` | Custom states for dynamic styling prefixes. |
| `semanticsLabel` | `String?` | `null` | Semantic label for accessibility. |
## Layout Modes
`WSvg` behaves as a fixed-size or flexible graphic depending on the classes provided.
### Sizing Priority
The widget determines its size based on the following priority:
1. `w-{n}` or `h-{n}` classes.
2. `text-{size}` classes (font size).
3. Inherited font size from the parent `DefaultTextStyle`.
```dart
WSvg(
src: 'assets/icon.svg',
className: 'w-10 h-10', // Explicit size
)
```
## Event Handling
`WSvg` does not handle events directly. To add interactivity, wrap it in a `WAnchor` or `WButton`.
```dart
WAnchor(
onTap: () => print('SVG clicked'),
child: WSvg(
src: 'assets/icon.svg',
className: 'hover:fill-blue-500 transition-colors',
),
)
```
## State Variants
Use state prefixes to change SVG styles based on interactions or environment:
```dart
WSvg(
src: 'assets/icon.svg',
className: 'fill-gray-400 hover:fill-blue-500 dark:fill-white',
)
```
## Styling Examples
### Color Priority
The widget applies a color filter using the following priority:
1. `stroke-{color}` (ideal for outlined icons)
2. `fill-{color}`
3. `text-{color}` (fallback)
4. Inherited color from parent
```dart
// Applying a stroke color to an outlined SVG
WSvg(
src: 'assets/outline-star.svg',
className: 'stroke-amber-500',
)
```
> [!NOTE]
> Adding the `preserve-colors` class bypasses the entire color priority chain. No `ColorFilter` is applied, and the SVG renders with its original embedded colors.
### Opacity and Transitions
You can apply opacity and animate it using transition classes.
```dart
WSvg(
src: 'assets/logo.svg',
className: 'opacity-50 hover:opacity-100 duration-300',
)
```
### Preserving Original Colors
By default, `WSvg` applies a `ColorFilter` based on the color priority chain above. For SVGs that contain multiple colors you want to keep intact—like QR codes, branded logos, or multi-color illustrations—use the `preserve-colors` utility class.
```dart
// Multi-color logo — keep original colors
WSvg(
src: 'assets/logo-colored.svg',
className: 'w-32 h-32 preserve-colors',
)
// QR code — must not be tinted
WSvg(
src: 'assets/qr-code.svg',
className: 'w-48 h-48 preserve-colors',
)
```
## All Supported Classes
| Category | Classes |
|:---------|:--------|
| Sizing | `w-{size}`, `h-{size}`, `text-{size}` |
| Coloring | `fill-{color}`, `stroke-{color}`, `text-{color}` |
| Preserve | `preserve-colors` |
| Opacity | `opacity-{n}` |
| Transitions| `duration-{ms}`, `ease-{curve}` |
| Animations | `animate-spin`, `animate-pulse`, `animate-bounce` |
## Customizing Theme
You can define custom colors in your `WindThemeData` that will be available to `WSvg`.
```dart
WindThemeData(
colors: {
'brand': Colors.blue,
},
)
```
Then use it as: `className: 'fill-brand-500'`.
## Related Documentation
- [WIcon](./w-icon.md)
- [WImage](./w-image.md)
- [SVG Parser](../parsers/svg_parser.md)
---
# WAnchor
# WAnchor
The foundational state wrapper that detects user gestures (Hover, Focus, Press) and propagates that state down to all descendant widgets via `WindAnchorStateProvider`.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WAnchor(
onTap: () => print('Pressed'),
child: WDiv(
// Reacts to hover state provided by WAnchor
className: 'p-4 bg-white hover:bg-gray-100 transition-colors',
child: WText('Hover Me'),
),
)
```
## Basic Usage
The `WAnchor` widget acts as the "brain" for interaction state management in Wind. It handles gesture detection and focus management, making it possible for child widgets to use prefixes like `hover:`, `focus:`, and `disabled:`.
Unlike most Wind widgets, `WAnchor` does not take a `className` itself. Instead, it provides the context needed for its children to respond to interactive states.
```dart
WAnchor(
onTap: () => print('Tapped!'),
child: WDiv(
className: 'p-4 bg-blue-500 hover:bg-blue-600 rounded-lg',
child: WText('Interactive Box', className: 'text-white'),
),
)
```
## Constructor
```dart
const WAnchor({
Key? key,
required Widget child,
VoidCallback? onTap,
VoidCallback? onLongPress,
VoidCallback? onDoubleTap,
bool isDisabled = false,
Set? states,
MouseCursor? mouseCursor,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `child` | `Widget` | **Required** | The widget that will receive the hover, focus, and gesture states. |
| `onTap` | `VoidCallback?` | `null` | Triggered when the widget is tapped. |
| `onLongPress` | `VoidCallback?` | `null` | Triggered when the widget is long-pressed. |
| `onDoubleTap` | `VoidCallback?` | `null` | Triggered when the widget is double-tapped. |
| `isDisabled` | `bool` | `false` | When true, gestures are ignored and the `disabled:` prefix is activated. |
| `states` | `Set?` | `null` | Custom states for dynamic styling (e.g., `{'active'}`). |
| `mouseCursor` | `MouseCursor?` | `null` | Custom cursor. Defaults to click when interactive. |
## Layout Modes
`WAnchor` is a logic wrapper and does not provide layout properties of its own. To control the layout of the interactive area, use a `WDiv` or another layout widget as the immediate child.
### Flex Layout Wrapper
```dart
WAnchor(
onTap: () {},
child: WDiv(
className: 'flex items-center gap-2 p-3 bg-gray-50 hover:bg-gray-100 rounded',
children: [
WIcon(Icons.add, className: 'text-blue-500'),
WText('Add Item'),
],
),
)
```
## Event Handling
`WAnchor` supports standard touch and mouse gestures. These events are only triggered if `isDisabled` is false.
```dart
WAnchor(
onTap: () => print('Single Tap'),
onDoubleTap: () => print('Double Tap'),
onLongPress: () => print('Long Press'),
child: WDiv(
className: 'p-10 bg-zinc-200 text-center',
child: WText('Gesture Interaction Area')
),
)
```
## State Variants
`WAnchor` enables several state prefixes for all Wind widgets in its subtree. This allows you to define complex interactive styles easily.
```dart
WAnchor(
isDisabled: false,
child: WDiv(
className: '''
bg-blue-500
hover:bg-blue-600
focus:ring-2 focus:ring-blue-300
disabled:bg-gray-400 disabled:opacity-50
''',
child: WText('Interactive States', className: 'text-white'),
),
)
```
## Styling Examples
### Card Lift Effect
```dart
WAnchor(
onTap: () {},
child: WDiv(
className: 'p-6 bg-white shadow hover:shadow-lg hover:-translate-y-1 rounded-xl duration-300',
child: WText('Hover to see the lift effect'),
),
)
```
### Navigation Link
```dart
WAnchor(
onTap: () {},
child: WDiv(
className: 'px-4 py-2 text-gray-600 hover:text-blue-600 hover:bg-blue-50 rounded',
child: WText('Dashboard'),
),
)
```
## All Supported Classes
While `WAnchor` does not take a `className`, it facilitates the use of these state prefixes for visual widgets (like `WDiv` or `WText`) within its subtree:
| Category | Prefixes / Features |
|:---------|:--------------------|
| Interaction | `hover:`, `focus:`, `disabled:` |
| Custom States | Any value passed to the `states` prop (e.g., `active:`, `error:`) |
| Gestures | Enables `onTap`, `onLongPress`, `onDoubleTap` |
## Customizing Theme
`WAnchor` behavior is primarily state-driven. You can customize the global interaction defaults via `WindThemeData`.
```dart
WindTheme(
data: WindThemeData(
// Theme scales influence the styles applied during states
baseSpacingUnit: 4.0,
),
child: MyApp(),
)
```
## Related Documentation
- [WButton](./w-button.md) - High-level button widget built on WAnchor.
- [WDiv](./w-div.md) - The primary layout widget used with WAnchor.
- [State Management](../core-concepts/state-management.md) - Deep dive into state prefixes.
---
# WButton
# WButton
`WButton` is an interactive component that combines `WAnchor` state management with Tailwind-like utility styling and built-in loading states.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WButton(
onTap: () => print('Button tapped'),
className: 'bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors',
child: WText('Click Me'),
)
```
## Basic Usage
The `WButton` widget handles common interactive states automatically. It provides a simple way to create buttons that react to hover, focus, and disabled states without manual state management.
```dart
WButton(
onTap: _submit,
className: 'bg-indigo-500 text-white p-3 rounded shadow hover:shadow-lg',
child: WText('Submit Form'),
)
```
## Constructor
```dart
const WButton({
Key? key,
required Widget child,
VoidCallback? onTap,
VoidCallback? onLongPress,
VoidCallback? onDoubleTap,
bool isLoading = false,
bool disabled = false,
String? className,
String? loadingText,
Widget? loadingWidget,
double loadingSize = 16,
Color? loadingColor,
Set? states,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `child` | `Widget` | **Required** | The button content (usually `WText` or `WIcon`). |
| `className` | `String?` | `null` | Wind utility classes for styling. |
| `onTap` | `VoidCallback?` | `null` | Callback when the button is tapped. Disabled if `isLoading` or `disabled` is true. |
| `onLongPress` | `VoidCallback?` | `null` | Callback for long press gestures. |
| `onDoubleTap` | `VoidCallback?` | `null` | Callback for double tap gestures. |
| `isLoading` | `bool` | `false` | Activates loading state: disables interaction and shows a spinner. |
| `disabled` | `bool` | `false` | Activates disabled state: disables interaction and changes cursor. |
| `loadingText` | `String?` | `null` | Text to display next to the loading spinner. |
| `loadingWidget` | `Widget?` | `null` | Custom widget to replace the default spinner. |
| `loadingSize` | `double` | `16` | Size of the default loading spinner. |
| `loadingColor` | `Color?` | `null` | Color of the spinner. Falls back to text color, then auto-computes contrast via W3C luminance when no color is resolvable. |
| `states` | `Set?` | `null` | Custom state prefixes (e.g., `{'error'}` for `error:` classes). |
## Layout Modes
`WButton` uses flexbox-like behavior internally. You can control content alignment using standard utility classes.
### Centered Content
By default, using `justify-center` in the `className` will center the child within the button's constraints.
```dart
WButton(
className: 'w-full bg-blue-500 justify-center py-3 text-white',
child: WText('Centered Full Width'),
)
```
## Event Handling
`WButton` exposes standard gesture callbacks. All interaction is automatically suppressed when `isLoading` or `disabled` is set to `true`.
```dart
WButton(
onTap: () => print('Tapped'),
onLongPress: () => print('Long Pressed'),
className: 'bg-gray-200 p-4',
child: WText('Press Me'),
)
```
## State Variants
`WButton` supports multiple state-based prefixes. These classes are applied only when the corresponding state is active.
```dart
WButton(
onTap: _submit,
className: 'bg-blue-600 hover:bg-blue-700 focus:ring-2 disabled:bg-gray-400 loading:opacity-50',
child: WText('Stateful Button'),
)
```
| Prefix | Trigger |
|:-------|:--------|
| `hover:` | When the mouse pointer is over the button. |
| `focus:` | When the button has keyboard focus. |
| `disabled:` | When the `disabled` prop is `true`. |
| `loading:` | When the `isLoading` prop is `true`. |
## Styling Examples
### Loading States
When `isLoading` is true, `WButton` replaces the `child` with a spinner. You can add text or a custom widget.
```dart
// Basic spinner
WButton(
isLoading: true,
className: 'bg-blue-500 text-white p-2',
child: WText('Login'),
)
// Spinner with text
WButton(
isLoading: true,
loadingText: 'Processing...',
className: 'bg-green-600 text-white px-4 py-2',
child: WText('Submit'),
)
```
> [!NOTE]
> When no explicit `loadingColor` is set and no text color is available from the className, the spinner automatically picks a contrasting color (light or dark) based on the button's background luminance using the W3C relative luminance algorithm.
### Icon Buttons
Combine `WButton` with `WIcon` for utility-styled icon buttons.
```dart
WButton(
className: 'bg-red-500 hover:bg-red-600 p-3 rounded-full text-white shadow-lg',
child: WIcon(Icons.delete),
)
```
## All Supported Classes
Since `WButton` uses `WDiv` and `WAnchor` internally, it supports the full range of Wind utilities.
| Category | Classes |
|:---------|:--------|
| Layout | `flex`, `grid`, `block`, `hidden`, `justify-center`, `items-center` |
| Spacing | `p-{n}`, `px-{n}`, `py-{n}`, `m-{n}`, `gap-{n}` |
| Sizing | `w-{size}`, `h-{size}`, `min-w-{size}`, `max-w-{size}` |
| Typography | `text-{size}`, `font-{weight}`, `text-{color}`, `uppercase` |
| Background | `bg-{color}`, `bg-gradient-to-{dir}`, `bg-opacity-{n}` |
| Borders | `border`, `border-{n}`, `rounded`, `rounded-{size}`, `ring` |
| Effects | `shadow`, `opacity-{n}`, `duration-{n}`, `ease-{type}` |
## Customizing Theme
You can customize the default behavior and appearance of buttons by modifying `WindThemeData`.
```dart
WindThemeData(
// Customize the base spacing unit used for p-4, m-2, etc.
baseSpacingUnit: 4.0,
// Add custom colors for specific button brands
colors: {
'brand': Colors.deepPurple,
},
)
```
## Related Documentation
- [WAnchor](./w-anchor.md) - The base interactive wrapper
- [WText](./w-text.md) - For styling button labels
- [Background Color](../styling/background-color.md) - Color utilities
- [Text Color](../typography/text-color.md) - Typography color utilities
- [Spacing](../layout/spacing.md) - Padding and margin utilities
---
# WPopover
# WPopover
A flexible popover component for creating dropdown menus, notification panels, user menus, tooltips, and similar overlay patterns.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WPopover(
alignment: PopoverAlignment.bottomRight,
className: 'w-64 bg-white dark:bg-gray-800 rounded-xl shadow-xl p-2',
triggerBuilder: (context, isOpen, isHovering) => WButton(
className: 'bg-blue-600 text-white',
child: Text('Open Menu'),
),
contentBuilder: (context, close) => Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(title: Text('Profile'), onTap: close),
ListTile(title: Text('Settings'), onTap: close),
],
),
)
```
## Basic Usage
The `WPopover` widget uses two builders: `triggerBuilder` for the element that activates the popover, and `contentBuilder` for the overlay content. It manages the overlay state and positioning automatically.
```dart
WPopover(
triggerBuilder: (context, isOpen, isHovering) => WText(
'Click me',
className: 'text-blue-500 cursor-pointer',
),
contentBuilder: (context, close) => WDiv(
className: 'p-4',
child: WText('Hello from Popover!'),
),
)
```
## Constructor
```dart
const WPopover({
Key? key,
required PopoverTriggerBuilder triggerBuilder,
required PopoverContentBuilder contentBuilder,
PopoverController? controller,
bool enableTriggerOnTap = true,
PopoverAlignment alignment = PopoverAlignment.bottomLeft,
String? className,
Offset offset = const Offset(0, 4),
double maxHeight = 400,
bool disabled = false,
bool closeOnContentTap = false,
VoidCallback? onOpen,
VoidCallback? onClose,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `triggerBuilder` | `PopoverTriggerBuilder` | **Required** | Builder for the trigger widget. Receives `isOpen` and `isHovering` states. |
| `contentBuilder` | `PopoverContentBuilder` | **Required** | Builder for the popover content. Receives a `close` callback. |
| `className` | `String?` | `null` | Wind utility classes for the popover container styling. |
| `controller` | `PopoverController?` | `null` | Optional controller for programmatic show/hide/toggle. |
| `alignment` | `PopoverAlignment` | `bottomLeft` | Where to position the popover relative to the trigger. |
| `offset` | `Offset` | `Offset(0, 4)` | Gap between the trigger and the popover. |
| `maxHeight` | `double` | `400` | Maximum height for the content. It will scroll if exceeded. |
| `enableTriggerOnTap` | `bool` | `true` | Whether tapping the trigger toggles the popover. |
| `closeOnContentTap` | `bool` | `false` | Whether tapping inside the content closes the popover. |
| `disabled` | `bool` | `false` | When true, the trigger will not respond to interactions. |
| `onOpen` | `VoidCallback?` | `null` | Callback fired when the popover is opened. |
| `onClose` | `VoidCallback?` | `null` | Callback fired when the popover is closed. |
| `autoFlip` | `bool` | `true` | When true, the popover flips its alignment to the opposite side if the natural position would render off-screen. |
## Layout Modes
While `WPopover` itself isn't a layout container, it supports various **Alignment Modes** to determine how the overlay is positioned relative to the trigger.
### Alignment
`WPopover` intelligently "flips" the alignment if the requested position would cause the overlay to overflow the screen edges.
| Alignment | Description |
|:----------|:------------|
| `bottomLeft` | Below trigger, aligned to left edge (Default). |
| `bottomRight` | Below trigger, aligned to right edge. |
| `bottomCenter` | Below trigger, centered horizontally. |
| `topLeft` | Above trigger, aligned to left edge. |
| `topRight` | Above trigger, aligned to right edge. |
| `topCenter` | Above trigger, centered horizontally. |
## Event Handling
The `triggerBuilder` provides the `isOpen` and `isHovering` states, allowing you to reactively style the trigger. The `contentBuilder` provides a `close` callback to dismiss the popover from within the content (e.g., when a menu item is clicked).
```dart
WPopover(
triggerBuilder: (context, isOpen, isHovering) => WDiv(
className: 'p-2 rounded ${isOpen ? "bg-blue-100" : "bg-gray-100"}',
child: WText(isOpen ? 'Active' : 'Idle'),
),
contentBuilder: (context, close) => WButton(
onTap: close,
child: Text('Click to Close'),
),
)
```
## State Variants
Since the popover trigger is built via `triggerBuilder`, you can apply state-based styling manually or use Wind's state prefixes if the trigger is a Wind widget like `WButton` or `WDiv`.
```dart
WPopover(
triggerBuilder: (context, isOpen, isHovering) => WButton(
// Use the isOpen boolean for manual state toggling
className: 'p-2 rounded-lg ${isOpen ? "bg-blue-600 text-white" : "bg-white text-gray-800"}',
child: Text('Toggle Menu'),
),
contentBuilder: (context, close) => MyContent(),
)
```
## Styling Examples
### Custom Size and Shadow
If no width is specified in `className`, the popover uses the trigger's width as a minimum. Use sizing utilities to define a specific width.
```dart
WPopover(
className: 'w-80 bg-white rounded-2xl shadow-2xl border border-gray-100',
triggerBuilder: (context, isOpen, isHovering) => Icon(Icons.more_horiz),
contentBuilder: (context, close) => MyLargeMenu(),
)
```
### Dark Mode
`WPopover` fully supports dark mode styling using the `dark:` prefix.
```dart
WPopover(
className: 'bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-800 shadow-xl',
triggerBuilder: (context, isOpen, isHovering) => MyTrigger(),
contentBuilder: (context, close) => MyContent(),
)
```
## All Supported Classes
The popover overlay container is a `WDiv` and supports all standard Wind utilities.
| Category | Classes |
|:---------|:--------|
| Sizing | `w-{size}`, `max-w-{size}`, `h-{size}`, `max-h-{size}` |
| Background | `bg-{color}`, `bg-opacity-{n}` |
| Borders | `border`, `border-{color}`, `rounded-{size}` |
| Shadow | `shadow-{size}`, `shadow-{color}/{opacity}` |
| Padding | `p-{n}`, `px-{n}`, `py-{n}` |
## Customizing Theme
Popovers inherit from global theme scales. You can customize the default appearance by modifying the `WindThemeData`.
```dart
WindThemeData(
colors: {
'popover-bg': Colors.white,
},
baseSpacingUnit: 4.0,
)
```
## Related Documentation
- [WSelect - A high-level dropdown built using WPopover](./w-select.md)
- [WDiv - The base container widget](../widgets/w-div.md)
- [WAnchor - Useful for interactive elements inside the popover](./w-anchor.md)
---
# WBreakpoint
# WBreakpoint
Declarative breakpoint-keyed builder for rendering different widget trees per responsive breakpoint. An **escape hatch** for cases where the widget structure genuinely differs between breakpoints — reach for className prefixes first.
- [Basic Usage](#basic-usage)
- [When to Use](#when-to-use)
- [Constructor](#constructor)
- [Props](#props)
- [Resolution Semantics](#resolution-semantics)
- [Custom Breakpoints](#custom-breakpoints)
- [Nested Theme Overrides](#nested-theme-overrides)
- [Related Documentation](#related-documentation)
```dart
WBreakpoint(
base: (ctx) => const MobileLayout(),
md: (ctx) => const TabletLayout(),
lg: (ctx) => const DesktopLayout(),
)
```
## Basic Usage
`WBreakpoint` picks one widget tree per breakpoint using Wind's own breakpoint resolution. `base` is required; other builders are optional fallbacks.
```dart
WBreakpoint(
base: (ctx) => WDiv(
className: 'flex flex-col gap-2',
children: [/* stacked cards */],
),
md: (ctx) => WDiv(
className: 'grid grid-cols-3 gap-4',
children: [/* grid cards */],
),
)
```
## When to Use
Prefer className-first patterns. `WBreakpoint` is a last resort:
| Need | Prefer |
|:-----|:-------|
| Swap styles per breakpoint | `sm:flex-row`, `md:gap-8` |
| Swap visibility per breakpoint | `hidden sm:block` / `block sm:hidden` |
| Reorder flex children | `order-2 md:order-1` |
| Render different widget **types** | `WBreakpoint` |
| Render different child **counts** | `WBreakpoint` |
If your two branches differ only in className, use prefixes instead — you'll get less code and a cleaner diff.
## Constructor
```dart
const WBreakpoint({
Key? key,
required WidgetBuilder base,
WidgetBuilder? sm,
WidgetBuilder? md,
WidgetBuilder? lg,
WidgetBuilder? xl,
WidgetBuilder? xxl,
Map custom = const {},
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `base` | `WidgetBuilder` | **Required** | Fallback builder when no higher breakpoint matches. |
| `sm` | `WidgetBuilder?` | `null` | Builder for `sm` breakpoint (default min width 640). |
| `md` | `WidgetBuilder?` | `null` | Builder for `md` breakpoint (default min width 768). |
| `lg` | `WidgetBuilder?` | `null` | Builder for `lg` breakpoint (default min width 1024). |
| `xl` | `WidgetBuilder?` | `null` | Builder for `xl` breakpoint (default min width 1280). |
| `xxl` | `WidgetBuilder?` | `null` | Builder for `2xl` breakpoint (default min width 1536). |
| `custom` | `Map` | `{}` | Builders for custom breakpoint keys defined in `WindThemeData.screens`. |
## Resolution Semantics
1. Read the active breakpoint from the `WindContext`.
2. Collect all breakpoints defined in `WindThemeData.screens` that are at or below the active width.
3. Sort them descending and pick the first one that has a builder.
4. Fall back to `base` if nothing matches.
Given default screens and width `1200` (active = `lg`):
```dart
WBreakpoint(
base: (_) => A(),
sm: (_) => B(),
md: (_) => C(),
)
// Resolution walks lg → md → sm. md has a builder → renders C().
```
## Custom Breakpoints
Define your own screen in `WindThemeData.screens` and drive `WBreakpoint` through the `custom` map:
```dart
WindTheme(
data: WindThemeData().copyWith(
screens: const {
'sm': 640,
'md': 768,
'tablet': 900, // custom
'lg': 1024,
'xl': 1280,
'2xl': 1536,
},
),
child: WBreakpoint(
base: (_) => const MobileTable(),
custom: {'tablet': (_) => const TabletTable()},
lg: (_) => const DesktopTable(),
),
)
```
Custom keys participate in the same descending resolution as built-ins.
## Nested Theme Overrides
`WBreakpoint` reads through any `WindTheme` ancestor, so a nested `WindTheme` override with its own `screens` map applies inside that subtree. Useful for design-system playgrounds and test harnesses.
## Related Documentation
- [Responsive Design](../layout/responsive.md)
- [Flexbox & Layout](../layout/flexbox.md)
- [WindTheme](../theme/wind-theme.md)
---
# WDynamic
# WDynamic
`WDynamic` renders a Flutter widget tree from a JSON/Map configuration at runtime, with built-in action handling and form state management.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [JSON Schema](#json-schema)
- [Action Handling](#action-handling)
- [State Management](#state-management)
- [Custom Builders](#custom-builders)
- [Security](#security)
- [Error Handling](#error-handling)
- [Related Documentation](#related-documentation)
```dart
WDynamic(
json: const {
'type': 'WDiv',
'props': {
'className': 'p-6 bg-white rounded-xl shadow-sm'
},
'children': [
{
'type': 'WText',
'props': {
'text': 'Hello from JSON!',
'className': 'text-xl font-bold text-gray-800'
}
},
],
},
)
```
## Basic Usage
The `WDynamic` widget converts JSON/Map structures into live Flutter widgets. This enables server-driven UI, dynamic form generation, and runtime-configurable layouts without rebuilding your app.
```dart
WDynamic(
json: const {
'type': 'WDiv',
'props': {'className': 'flex gap-4 p-4'},
'children': [
{
'type': 'WButton',
'props': {
'className': 'bg-blue-500 text-white px-4 py-2 rounded',
'onTap': {'action': 'submit'},
},
'children': [
{'type': 'WText', 'props': {'text': 'Submit'}}
],
}
],
},
actions: {
'submit': (args) => print('Button tapped'),
},
)
```
## Constructor
```dart
const WDynamic({
Key? key,
required Map json,
Map actions = const {},
WDynamicController? controller,
Set? denyWidgets,
Map? builders,
Map? customIcons,
int maxDepth = 50,
Widget Function(String type, Object error)? onError,
Widget Function(String type, Map props)? onUnknownWidget,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `json` | `Map` | **Required** | JSON configuration defining the widget tree. Must include `type`, optional `props` and `children`. |
| `actions` | `Map` | `{}` | Action handlers keyed by name. Called when interactive widgets trigger events. |
| `controller` | `WDynamicController?` | `null` | Optional controller for external state access (read/write form values). |
| `denyWidgets` | `Set?` | `null` | Widget types to remove from the default whitelist. |
| `builders` | `Map?` | `null` | Custom widget builders keyed by type name. Signature: `Widget Function(Map props, List children)`. |
| `customIcons` | `Map?` | `null` | Custom icon mappings for `WIcon` widgets. Keys are icon names used in the JSON `icon` prop, values are `IconData` instances. Extends the default Material icon map. |
| `maxDepth` | `int` | `50` | Maximum recursion depth for nested widgets. Prevents infinite loops. |
| `onError` | `Widget Function(String, Object)?` | `null` | Error handler for widget build failures. Returns a fallback widget. |
| `onUnknownWidget` | `Widget Function(String, Map)?` | `null` | Handler for unknown or denied widget types. Returns a fallback widget. |
## JSON Schema
Every widget node in the JSON structure follows this format:
```dart
{
'type': 'WidgetType', // Required: Wind widget, Flutter widget, or custom builder
'props': { // Optional: properties passed to the widget
'className': 'flex gap-4',
'text': 'Hello',
'onTap': {'action': 'handleTap', 'args': {'id': 1}},
'id': 'username', // Auto-tracked form state key
},
'children': [ // Optional: nested widget array
{ /* child node */ }
]
}
```
### Type Field
The `type` field determines which widget to render:
- **Wind Widgets**: `WDiv`, `WText`, `WButton`, `WInput`, `WCheckbox`, `WSelect`, `WDatePicker`, `WIcon`, `WImage`, `WSvg`, `WPopover`, `WAnchor`, `WSpacer`
- **Flutter Widgets**: `Column`, `Row`, `Center`, `SizedBox`, `Expanded`, `Container`, `Wrap`, `Stack`, `Positioned`, `Padding`, `Align`, `Opacity`, `AspectRatio`, `FittedBox`, `ClipRRect`, `Spacer`
- **Custom Widgets**: Any key from the `builders` map
### Props Field
Widget properties are passed directly to the constructor. Common props include:
```dart
{
'type': 'WDiv',
'props': {
'className': 'flex gap-4 p-6 bg-white rounded-xl', // Wind utilities
'id': 'formContainer', // Auto-tracked state key
}
}
```
```dart
{
'type': 'WText',
'props': {
'text': 'Welcome!', // Text content
'className': 'text-lg font-bold', // Wind utilities
}
}
```
```dart
{
'type': 'WInput',
'props': {
'id': 'email', // Auto-tracked in controller state
'placeholder': 'Enter email...',
'className': 'border rounded-lg',
}
}
```
### Children Field
An array of nested widget nodes. Used by layout widgets like `WDiv`, `Column`, `Row`, etc.
```dart
{
'type': 'WDiv',
'props': {'className': 'flex gap-2'},
'children': [
{'type': 'WText', 'props': {'text': 'First'}},
{'type': 'WText', 'props': {'text': 'Second'}},
]
}
```
## Action Handling
Interactive widgets like `WButton` can trigger actions defined in the `actions` map. Actions are referenced in the JSON using an object format.
### Action Format
```dart
{
'type': 'WButton',
'props': {
'onTap': {
'action': 'actionName', // Required: key from actions map
'args': {'key': 'value'} // Optional: arguments passed to handler
}
}
}
```
### Action Signatures
Actions can accept arguments only, or both arguments and state:
```dart
// Simple: (Map args)
WDynamic(
actions: {
'showAlert': (Map args) {
print('Alert: ${args['message']}');
},
},
)
```
```dart
// With state access: (Map args, WDynamicState state)
WDynamic(
actions: {
'submitForm': (Map args, WDynamicState state) {
final email = state.get('email');
final password = state.get('password');
print('Submitting: $email');
},
},
)
```
### Example
```dart
WDynamic(
json: const {
'type': 'WButton',
'props': {
'className': 'bg-blue-500 text-white px-4 py-2 rounded',
'onTap': {
'action': 'increment',
'args': {'step': 1}
},
},
'children': [
{'type': 'WText', 'props': {'text': 'Tap Me'}}
],
},
actions: {
'increment': (Map args, WDynamicState state) {
final step = args['step'] ?? 1;
setState(() => count += step);
},
},
)
```
## State Management
`WDynamic` automatically tracks form widget values using the `id` prop. Values are stored in an internal state object accessible via `WDynamicController` or action handlers.
### Auto-Tracking with ID
Any widget with an `id` prop automatically stores its value in state:
```dart
{
'type': 'WInput',
'props': {
'id': 'username', // Auto-tracked
'placeholder': 'Username...',
}
}
```
### Using the Controller
Create a `WDynamicController` to access state externally:
```dart
final controller = WDynamicController();
// Pre-fill values
controller.setValue('email', 'user@example.com');
// Read values
final email = controller.getValue('email');
final allValues = controller.getAll();
// Listen for changes
final dispose = controller.addListener('email', (value) {
print('Email changed: $value');
});
// Pass to WDynamic
WDynamic(
controller: controller,
json: myJson,
)
// Clean up
controller.dispose();
```
### Accessing State in Actions
Action handlers receive the state object as a second parameter:
```dart
WDynamic(
json: const {
'type': 'WDiv',
'children': [
{
'type': 'WInput',
'props': {'id': 'name', 'placeholder': 'Your name...'}
},
{
'type': 'WButton',
'props': {
'onTap': {'action': 'greet'},
'className': 'bg-green-500 text-white px-4 py-2 rounded',
},
'children': [
{'type': 'WText', 'props': {'text': 'Greet'}}
],
}
],
},
actions: {
'greet': (args, state) {
final name = state.get('name') ?? 'World';
print('Hello, $name!');
},
},
)
```
## Custom Builders
Extend `WDynamic` with custom widget types using the `builders` prop.
### WWidgetBuilder Signature
```dart
typedef WWidgetBuilder = Widget Function(
Map props,
List children,
);
```
### Example
```dart
WDynamic(
json: const {
'type': 'InfoCard',
'props': {
'title': 'Custom Widget',
'subtitle': 'Built with a custom builder'
},
},
builders: {
'InfoCard': (Map props, List children) {
return WDiv(
className: 'p-4 bg-blue-50 rounded-xl border border-blue-200',
child: WDiv(
className: 'flex items-center gap-3',
children: [
WIcon(Icons.info, className: 'text-blue-500 text-2xl'),
WDiv(
className: 'flex flex-col',
children: [
WText(props['title'] ?? '', className: 'font-bold text-blue-800'),
WText(props['subtitle'] ?? '', className: 'text-sm text-blue-600'),
],
),
],
),
);
},
},
)
```
Custom builders have priority over default widgets. You can override built-in widgets by providing a builder with the same type name.
### Custom Icons
Map string icon names to `IconData` for use in JSON `WIcon` widget nodes. Custom icons extend (not replace) the built-in Material icon map.
```dart
WDynamic(
json: const {
'type': 'WDiv',
'props': {'className': 'flex gap-4 items-center'},
'children': [
{
'type': 'WIcon',
'props': {'icon': 'heart', 'className': 'text-red-500 text-2xl'},
},
{
'type': 'WIcon',
'props': {'icon': 'star', 'className': 'text-yellow-500 text-2xl'},
},
],
},
customIcons: {
'heart': Icons.favorite,
'star': Icons.star,
'settings': Icons.settings,
},
)
```
## Security
`WDynamic` uses a whitelist approach for security. Only explicitly allowed widget types can be rendered.
### Default Whitelist
**Wind Widgets**:
- `WDiv`, `WText`, `WButton`, `WImage`, `WIcon`, `WAnchor`, `WInput`, `WCheckbox`, `WSvg`, `WSelect`, `WPopover`, `WDatePicker`, `WSpacer`
**Flutter Widgets**:
- `Column`, `Row`, `Center`, `SizedBox`, `Expanded`, `Container`, `Wrap`, `Stack`, `Positioned`, `Padding`, `Align`, `Opacity`, `AspectRatio`, `FittedBox`, `ClipRRect`, `Spacer`
### Denying Widgets
Remove specific widget types from the whitelist:
```dart
WDynamic(
json: myJson,
denyWidgets: const {'WButton', 'WInput'}, // Block buttons and inputs
)
```
### Unknown Widget Handler
Provide a fallback for unknown or denied widget types:
```dart
WDynamic(
json: myJson,
onUnknownWidget: (String type, Map props) {
return WDiv(
className: 'p-2 bg-red-100 border border-red-300 rounded',
child: WText('Unknown widget: $type', className: 'text-red-700 text-sm'),
);
},
)
```
## Error Handling
Handle widget build failures gracefully with the `onError` callback.
```dart
WDynamic(
json: myJson,
onError: (String type, Object error) {
return WDiv(
className: 'p-4 bg-yellow-50 border border-yellow-300 rounded',
child: WDiv(
className: 'flex flex-col gap-2',
children: [
WText('Error rendering $type', className: 'font-bold text-yellow-800'),
WText(error.toString(), className: 'text-sm text-yellow-700'),
],
),
);
},
)
```
### Max Depth Protection
Prevent infinite recursion with the `maxDepth` prop (default: 50):
```dart
WDynamic(
json: deeplyNestedJson,
maxDepth: 100, // Allow deeper nesting
)
```
When max depth is exceeded, rendering stops and returns an empty `SizedBox.shrink()` or calls `onError` if provided.
## Related Documentation
- [WDiv](./w-div.md) - Primary layout container
- [WButton](./w-button.md) - Interactive button widget
- [WInput](./w-input.md) - Text input widget
- [WSelect](./w-select.md) - Dropdown select widget
- [State Management](../core-concepts/state-management.md) - State handling in Wind
- [Dynamic Rendering](../core-concepts/dynamic-rendering.md) - Server-driven UI concepts
---
# WCheckbox
# WCheckbox
A utility-first checkbox component that translates Tailwind-style classes into a fully customizable checkbox widget. It sidesteps the limitations of native Flutter checkboxes by using a composable architecture of `WAnchor` and `WDiv`.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WCheckbox(
value: isChecked,
onChanged: (val) => setState(() => isChecked = val),
className: 'w-6 h-6 rounded-md border-gray-400 checked:bg-indigo-600',
)
```
## Basic Usage
The `WCheckbox` widget provides a highly flexible alternative to native Flutter checkboxes. It supports all Wind utility classes, allowing for complex styling like custom borders, focus rings, and state-based colors using the `checked:` prefix.
```dart
WCheckbox(
value: _value,
onChanged: (val) => setState(() => _value = val),
className: 'w-5 h-5 rounded border-2 border-slate-300 checked:bg-blue-500 checked:border-transparent',
)
```
## Constructor
```dart
const WCheckbox({
Key? key,
required bool value,
ValueChanged? onChanged,
String? className,
String? iconClassName,
bool disabled = false,
IconData? checkIcon,
Set? states,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `value` | `bool` | - | Whether the checkbox is currently checked. |
| `onChanged` | `ValueChanged?` | `null` | Callback triggered when the checkbox state is toggled. |
| `className` | `String?` | `null` | Wind utility classes for the checkbox container (dimensions, border, background). |
| `iconClassName` | `String?` | `null` | Utility classes applied to the check icon (e.g., `'text-white text-xs'`). |
| `disabled` | `bool` | `false` | When true, prevents interaction and applies the `disabled:` prefix styles. |
| `checkIcon` | `IconData?` | `null` | Custom icon shown when `value` is true. When `null`, the widget renders `Icons.check`. |
| `states` | `Set?` | `null` | Custom state identifiers merged with built-in `checked` and `disabled` states. |
## Layout Modes
### Dimensions
Unlike some widgets that grow to fit their content, a checkbox requires explicit width (`w-{n}`) and height (`h-{n}`) classes to define its clickable area and visual size.
```dart
WCheckbox(
value: true,
className: 'w-4 h-4 rounded-sm', // Small checkbox
)
WCheckbox(
value: true,
className: 'w-8 h-8 rounded-full', // Large circular checkbox
)
```
## Event Handling
The `onChanged` callback is triggered when the user taps the checkbox. It provides the toggled boolean value.
```dart
WCheckbox(
value: isChecked,
onChanged: (bool newValue) {
setState(() {
isChecked = newValue;
});
},
)
```
## State Variants
`WCheckbox` uses the `checked:` prefix to apply styles when the value is true. It also supports standard interaction prefixes like `hover:`, `focus:`, and `disabled:`.
```dart
WCheckbox(
value: isChecked,
className: 'border-gray-300 checked:bg-blue-600 checked:border-transparent hover:border-blue-400 disabled:opacity-50',
)
```
## Styling Examples
### Custom Icons and Colors
You can completely change the look of the checkbox by providing a custom icon and using background utilities.
```dart
WCheckbox(
value: true,
checkIcon: Icons.favorite,
className: 'w-6 h-6 bg-pink-100 border-pink-300 checked:bg-pink-500 checked:border-transparent',
iconClassName: 'text-white text-xs',
)
```
### Validation States
Using the `states` property allows you to apply conditional styling for errors or success states.
```dart
WCheckbox(
value: false,
states: {'error'},
className: 'border-gray-300 error:border-red-500 error:ring-2 error:ring-red-100',
)
```
## All Supported Classes
| Category | Classes |
|:---------|:--------|
| Sizing | `w-{n}`, `h-{n}`, `min-w-{n}`, `aspect-square` |
| Spacing | `p-{n}`, `m-{n}` |
| Appearance | `rounded`, `border`, `bg-{color}`, `shadow` |
| States | `checked:`, `hover:`, `focus:`, `disabled:`, `error:` |
| Effects | `opacity-{n}`, `ring`, `duration-{n}`, `ease-{n}` |
## Customizing Theme
The default checked color can be controlled via the `primary` color in your `WindThemeData`.
```dart
WindThemeData(
colors: {
'primary': context.windColors['indigo']!,
},
)
```
## Related Documentation
- [WFormCheckbox](./w-form-checkbox.md) - Form-integrated checkbox with label and validation.
- [WAnchor](./w-anchor.md) - The base interactive component used by WCheckbox.
- [WIcon](./w-icon.md) - Documentation for the check icon styling.
---
# WInput
# WInput
The `WInput` widget is a utility-first form input that combines React-style controlled state management with Tailwind-like styling. It replaces the standard `TextField` with a more flexible, composable alternative.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WInput(
value: _email,
onChanged: (value) => setState(() => _email = value),
type: InputType.email,
placeholder: 'Enter your email',
className: 'p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500',
placeholderClassName: 'text-gray-400 italic',
)
```
## Basic Usage
The `WInput` widget manages its internal state but can be easily controlled using the `value` and `onChanged` properties. By default, it renders as a standard text input.
```dart
WInput(
placeholder: 'Type something...',
className: 'w-full p-2 border rounded',
onChanged: (val) => print(val),
)
```
## Constructor
```dart
const WInput({
Key? key,
String? value,
ValueChanged? onChanged,
InputType type = InputType.text,
String? className,
String? placeholderClassName,
String? placeholder,
bool enabled = true,
bool readOnly = false,
bool autofocus = false,
TextInputAction? textInputAction,
ValueChanged? onSubmitted,
VoidCallback? onEditingComplete,
VoidCallback? onTap,
TapRegionCallback? onTapOutside,
int? maxLines,
int minLines = 1,
FocusNode? focusNode,
TextEditingController? controller,
Set? states,
List? inputFormatters,
TextCapitalization textCapitalization = TextCapitalization.none,
bool autocorrect = true,
bool enableSuggestions = true,
Widget? prefix,
Widget? suffix,
String? semanticLabel,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `className` | `String?` | `null` | Wind utility classes for the input field |
| `placeholderClassName` | `String?` | `null` | Utility classes for styling the hint text |
| `value` | `String?` | `null` | The controlled value of the input |
| `onChanged` | `ValueChanged?` | `null` | Callback when text changes |
| `type` | `InputType` | `InputType.text` | Input keyboard and behavior (`text`, `password`, `email`, `number`, `multiline`) |
| `placeholder` | `String?` | `null` | Hint text shown when input is empty |
| `enabled` | `bool` | `true` | Whether the input is interactive |
| `readOnly` | `bool` | `false` | Whether the input is read-only |
| `autofocus` | `bool` | `false` | Autofocus on mount |
| `textInputAction` | `TextInputAction?` | `null` | Keyboard action (e.g., `.done`, `.next`) |
| `onSubmitted` | `ValueChanged?` | `null` | Callback when action button is pressed |
| `onTapOutside` | `TapRegionCallback?` | `null` | Callback when tapping outside (useful for blur) |
| `maxLines` | `int?` | `null` | Max lines for multiline input |
| `minLines` | `int` | `1` | Min lines for multiline input |
| `prefix` | `Widget?` | `null` | Widget displayed before text |
| `suffix` | `Widget?` | `null` | Widget displayed after text |
| `controller` | `TextEditingController?` | `null` | Optional external controller |
| `states` | `Set?` | `null` | Custom states for dynamic styling |
| `onEditingComplete` | `VoidCallback?` | `null` | Callback fired when the user finishes editing (loses focus, presses done, etc.) |
| `onTap` | `VoidCallback?` | `null` | Callback fired when the field is tapped |
| `focusNode` | `FocusNode?` | `null` | External focus controller for programmatic focus management |
| `inputFormatters` | `List?` | `null` | Formatters applied as the user types (digit-only, masks, etc.) |
| `textCapitalization` | `TextCapitalization` | `TextCapitalization.none` | Auto-capitalize behavior (`none` / `sentences` / `words` / `characters`) |
| `autocorrect` | `bool` | `true` | Enable OS autocorrect suggestions |
| `enableSuggestions` | `bool` | `true` | Enable OS suggestion bar (Android) |
| `semanticLabel` | `String?` | `null` | Accessibility label exposed via `Semantics(textField: true, label: ...)`. Falls back to `placeholder` when null. |
## Layout Modes
### Multiline Input
For text areas, set the `type` to `InputType.multiline`. You can control the height using `minLines` or standard sizing classes.
```dart
WInput(
type: InputType.multiline,
minLines: 3,
maxLines: 5,
className: 'w-full p-4 border rounded-xl bg-gray-50',
placeholder: 'Enter your message...',
)
```
## Event Handling
`WInput` provides standard callbacks for user interaction. Use `onSubmitted` for handling "Enter" or keyboard action buttons.
```dart
WInput(
className: 'border p-2',
textInputAction: TextInputAction.search,
onSubmitted: (value) {
_performSearch(value);
},
onTapOutside: (_) => FocusScope.of(context).unfocus(),
)
```
## State Variants
Wind automatically manages `focus:` and `disabled:` states. You can also trigger custom states like `error:` by passing them to the `states` property.
```dart
WInput(
states: _hasError ? {'error'} : {},
className: 'border border-gray-300 focus:border-blue-500 error:border-red-500',
)
```
> [!NOTE]
> `focus:` styles (like `ring-2`) are applied automatically when the field gains focus.
## Styling Examples
### Search Input with Prefix
You can use the `prefix` prop to add icons while maintaining the utility-first styling of the input.
```dart
WInput(
prefix: Icon(Icons.search, color: Colors.gray),
className: 'pl-10 p-2 bg-gray-100 rounded-full border-transparent focus:bg-white focus:border-blue-500',
placeholder: 'Search...',
)
```
### Password Field
Use `InputType.password` to obscure text. You can use the `suffix` prop to add a visibility toggle.
```dart
WInput(
type: _obscure ? InputType.password : InputType.text,
suffix: IconButton(
icon: Icon(_obscure ? Icons.visibility : Icons.visibility_off),
onPressed: () => setState(() => _obscure = !_obscure),
),
className: 'p-3 border rounded',
)
```
## All Supported Classes
| Category | Classes |
|:---------|:--------|
| Layout | `w-{size}`, `h-{size}`, `flex-1`, `w-full` |
| Spacing | `p-{n}`, `px-{n}`, `py-{n}`, `m-{n}` |
| Typography | `text-{size}`, `font-{weight}`, `italic`, `uppercase` |
| Colors | `bg-{color}`, `text-{color}` |
| Borders | `border`, `border-{n}`, `rounded-{size}`, `border-{color}` |
| Effects | `shadow-{size}`, `opacity-{n}`, `ring-{n}`, `ring-offset-{n}` |
## Customizing Theme
You can override the default input appearance through `WindThemeData`. This is useful for setting global border colors or padding defaults.
```dart
WindThemeData(
colors: {
'input-border': Colors.blueGrey,
},
// WInput respects global spacing units for p-x classes
baseSpacingUnit: 4.0,
)
```
## Related Documentation
- [WFormInput](./w-form-input.md) - Form-integrated input with labels and validation.
- [WButton](./w-button.md) - Interactive button component.
- [WText](./w-text.md) - Typography component.
---
# WSelect
# WSelect
A highly customizable, utility-first select component that supports single selection, multi-selection, searching, and remote data fetching.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WSelect(
value: _selected,
options: [
SelectOption(value: 'dart', label: 'Dart'),
SelectOption(value: 'flutter', label: 'Flutter'),
],
onChange: (value) => setState(() => _selected = value),
className: 'w-64 bg-white border border-gray-300 rounded-lg',
)
```
## Basic Usage
The `WSelect` widget replaces the standard Material Dropdown with a utility-first approach. It manages its own open state while providing a controlled interface for selection.
```dart
WSelect(
placeholder: 'Choose a number',
options: List.generate(5, (i) => SelectOption(value: i, label: 'Option $i')),
onChange: (val) => print('Selected: $val'),
className: 'bg-white border p-3 rounded-md',
)
```
## Constructor
```dart
const WSelect({
Key? key,
T? value,
ValueChanged? onChange,
bool isMulti = false,
List? values,
ValueChanged>? onMultiChange,
SelectedChipBuilder? selectedChipBuilder,
required List> options,
bool searchable = false,
Future>> Function(String)? onSearch,
String searchPlaceholder = 'Search...',
Future> Function(String)? onCreateOption,
CreateOptionBuilder? createOptionBuilder,
Future>> Function()? onLoadMore,
bool hasMore = false,
String? className,
String? menuClassName,
String placeholder = 'Select an option',
bool disabled = false,
double? menuWidth,
double maxMenuHeight = 300,
Set? states,
SelectTriggerBuilder? triggerBuilder,
MultiSelectTriggerBuilder? multiTriggerBuilder,
SelectItemBuilder? itemBuilder,
EmptyStateBuilder? emptyBuilder,
LoadingBuilder? loadingBuilder,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `className` | `String?` | `null` | Utility classes for the trigger container |
| `menuClassName` | `String?` | `null` | Utility classes for the dropdown menu |
| `options` | `List>` | **Required** | List of items to display |
| `value` | `T?` | `null` | Selected value in single-select mode |
| `onChange` | `ValueChanged?` | `null` | Callback for single selection changes |
| `isMulti` | `bool` | `false` | Enables multi-select mode |
| `values` | `List?` | `null` | Selected values in multi-select mode |
| `onMultiChange` | `ValueChanged>?` | `null` | Callback for multi-selection changes |
| `searchable` | `bool` | `false` | Shows a search input in the menu |
| `searchPlaceholder` | `String` | `'Search...'` | Placeholder text in the search input |
| `onSearch` | `Future>> Function(String)?` | `null` | Async remote search handler; returns filtered options for the query |
| `onCreateOption` | `Future> Function(String)?` | `null` | Tagging handler; called when the user creates a new option from search input |
| `createOptionBuilder` | `CreateOptionBuilder?` | `null` | Custom builder for the "create new option" row in the menu |
| `onLoadMore` | `Future>> Function()?` | `null` | Pagination handler; called when the user scrolls past the current list |
| `hasMore` | `bool` | `false` | When true, indicates more pages are available for `onLoadMore` |
| `placeholder` | `String` | `'Select an option'` | Text shown when no value is selected |
| `disabled` | `bool` | `false` | Prevents interaction |
| `menuWidth` | `double?` | `null` | Fixed dropdown width; defaults to the trigger width |
| `maxMenuHeight`| `double` | `300` | Maximum height of the dropdown list |
| `states` | `Set?` | `null` | Custom states for dynamic styling |
| `triggerBuilder` | `SelectTriggerBuilder?` | `null` | Custom trigger renderer for single-select |
| `multiTriggerBuilder` | `MultiSelectTriggerBuilder?` | `null` | Custom trigger renderer for multi-select |
| `selectedChipBuilder` | `SelectedChipBuilder?` | `null` | Custom chip renderer for selected items in multi-select |
| `itemBuilder` | `SelectItemBuilder?` | `null` | Custom renderer for each option in the menu |
| `emptyBuilder` | `EmptyStateBuilder?` | `null` | Custom widget when no options match the search |
| `loadingBuilder` | `LoadingBuilder?` | `null` | Custom widget shown while `onSearch` or `onLoadMore` is pending |
## Layout Modes
### Single Select
The default mode for selecting a single item. It displays the label of the selected option in the trigger.
```dart
WSelect(
value: 'apple',
options: [
SelectOption(value: 'apple', label: 'Apple'),
SelectOption(value: 'banana', label: 'Banana'),
],
onChange: (v) => print(v),
)
```
### Multi Select
Enable `isMulti: true` to allow multiple selections. By default, it displays selected items as chips.
```dart
WSelect(
isMulti: true,
values: ['red', 'blue'],
options: [
SelectOption(value: 'red', label: 'Red'),
SelectOption(value: 'blue', label: 'Blue'),
SelectOption(value: 'green', label: 'Green'),
],
onMultiChange: (list) => print(list),
)
```
## Event Handling
`WSelect` provides callbacks for selection changes and remote interactions.
```dart
WSelect(
onChange: (value) {
// Handle single selection
},
onSearch: (query) async {
// Return list of options based on search query
return fetchRemoteOptions(query);
},
onLoadMore: () async {
// Load next page of options
return fetchNextPage();
},
)
```
## State Variants
`WSelect` automatically manages several states that you can style using prefixes in `className`:
- `hover:` - When the mouse is over the trigger.
- `focus:` - When the dropdown is open.
- `disabled:` - When the widget is disabled.
- `selected:` - When a value is selected.
```dart
WSelect(
className: 'border-gray-300 hover:border-blue-500 focus:ring-2 focus:ring-blue-200',
)
```
## Styling Examples
### Searchable Select
Combine `searchable: true` with custom menu styling for a robust selection experience.
```dart
WSelect(
searchable: true,
searchPlaceholder: 'Search countries...',
menuClassName: 'bg-white shadow-xl rounded-xl border border-gray-100',
options: countries,
onChange: (val) => _country = val,
)
```
### Tagging (Create Option)
Allow users to create new options if a search yields no results.
```dart
WSelect(
searchable: true,
onCreateOption: (query) async {
final newOpt = SelectOption(value: query.toLowerCase(), label: query);
// Add to your local state/database
return newOpt;
},
options: existingTags,
)
```
## All Supported Classes
| Category | Classes |
|:---------|:--------|
| Layout | `flex`, `hidden`, `w-{size}`, `h-{size}` |
| Spacing | `p-{n}`, `m-{n}`, `gap-{n}` |
| Visuals | `bg-{color}`, `border`, `rounded`, `shadow` |
| States | `hover:`, `focus:`, `disabled:`, `dark:` |
| Custom | `open:`, `selected:` |
## Customizing Theme
The default styling for `WSelect` and its chips can be influenced by the `WindThemeData`.
```dart
WindThemeData(
colors: {
'primary': Colors.indigo,
},
)
```
## Related Documentation
- [WFormSelect](./w-form-select.md) - Form-integrated version with validation
- [WPopover](./w-popover.md) - The underlying overlay engine
- [WInput](./w-input.md) - Used for the internal search field
---
# WDatePicker
# WDatePicker
A utility-first date picker component built on [WPopover](./w-popover.md) with support for single date selection, date range selection, min/max constraints, and custom display formatting.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Types](#types)
- [Date Range Selection](#date-range-selection)
- [Min/Max Constraints](#minmax-constraints)
- [Custom Display Format](#custom-display-format)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [Calendar Internals](#calendar-internals)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WDatePicker(
value: _selectedDate,
onChanged: (date) => setState(() => _selectedDate = date),
className: 'w-full p-3 border border-gray-300 rounded-lg',
placeholder: 'Select a date',
)
```
## Basic Usage
`WDatePicker` renders a trigger container that opens a popover-based calendar on click. It handles month navigation, today highlighting, and date constraints out of the box.
```dart
DateTime? _selectedDate;
WDatePicker(
value: _selectedDate,
onChanged: (date) {
setState(() => _selectedDate = date);
},
className: 'bg-white border rounded-md px-4 py-2 hover:border-blue-500',
)
```
When no `className` is provided, the trigger uses this default styling:
```dart
// Default trigger className
'bg-white border border-gray-300 rounded-lg p-3 dark:bg-gray-800 dark:border-gray-600'
```
The calendar popover opens below the trigger and auto-flips if there isn't enough space. In single mode, the popover closes automatically after a date is selected.
## Constructor
```dart
const WDatePicker({
Key? key,
DatePickerMode mode = DatePickerMode.single,
DateTime? value,
DateRange? range,
ValueChanged? onChanged,
ValueChanged? onRangeChanged,
DateTime? minDate,
DateTime? maxDate,
String? className,
String placeholder = 'Select date',
bool disabled = false,
Set states = const {},
DateDisplayFormat? displayFormat,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `mode` | `DatePickerMode` | `single` | Selection mode: `single` or `range` |
| `value` | `DateTime?` | `null` | Currently selected date (single mode) |
| `range` | `DateRange?` | `null` | Currently selected range (range mode) |
| `onChanged` | `ValueChanged?` | `null` | Callback fired on date selection (single mode) |
| `onRangeChanged` | `ValueChanged?` | `null` | Callback fired on range selection (range mode) |
| `minDate` | `DateTime?` | `null` | Earliest selectable date |
| `maxDate` | `DateTime?` | `null` | Latest selectable date |
| `className` | `String?` | `null` | Wind utility classes for the trigger container |
| `placeholder` | `String` | `'Select date'` | Text shown when no value is selected |
| `disabled` | `bool` | `false` | Prevents interaction, shows forbidden cursor |
| `states` | `Set` | `const {}` | Custom states for dynamic styling |
| `displayFormat` | `DateDisplayFormat?` | `null` | Custom function to format dates for display |
## Types
### DatePickerMode
Determines if the picker operates in single date or date range selection mode.
```dart
enum DatePickerMode {
single, // Pick a single date
range, // Pick a start and end date
}
```
### DateRange
Represents a date range with a required start and optional end date.
```dart
class DateRange {
final DateTime start;
final DateTime? end;
bool get isComplete => end != null;
DateRange copyWith({DateTime? start, DateTime? end});
}
```
### DateDisplayFormat
A typedef for custom date formatting:
```dart
typedef DateDisplayFormat = String Function(DateTime date);
```
When no `displayFormat` is provided, dates display as `"Jan 15, 2025"` format.
## Date Range Selection
Setting `mode: DatePickerMode.range` enables two-click range selection with hover preview.
```dart
DateRange? _dateRange;
WDatePicker(
mode: DatePickerMode.range,
range: _dateRange,
onRangeChanged: (range) => setState(() => _dateRange = range),
placeholder: 'Check-in / Check-out',
className: 'w-64 border p-3 rounded-lg',
)
```
How range selection works:
1. **First click** — Sets the range start. The `onRangeChanged` callback fires with a `DateRange` where `end` is `null`.
2. **Hover** — As the user moves the mouse, dates between start and the hovered date are highlighted with a blue tint.
3. **Second click** — Completes the range. If the second date is before the first, they're automatically swapped. The popover closes.
The trigger display text updates throughout: `"Jan 15, 2025 - ..."` while in progress, then `"Jan 15, 2025 - Jan 20, 2025"` when complete.
## Min/Max Constraints
Use `minDate` and `maxDate` to restrict which dates are selectable. Dates outside the range appear dimmed and don't respond to clicks.
```dart
WDatePicker(
value: _selectedDate,
onChanged: (date) => setState(() => _selectedDate = date),
minDate: DateTime.now(),
maxDate: DateTime.now().add(const Duration(days: 90)),
className: 'p-3 border rounded-lg',
placeholder: 'Next 90 days only',
)
```
> [!NOTE]
> Constraints are compared at day-level granularity. Time components are stripped before comparison.
## Custom Display Format
Override the default `"Jan 15, 2025"` format with a custom function:
```dart
WDatePicker(
value: _selectedDate,
onChanged: (date) => setState(() => _selectedDate = date),
displayFormat: (date) => '${date.day}/${date.month}/${date.year}',
className: 'p-3 border rounded-lg',
)
```
In range mode, the format function applies to both start and end dates independently.
## Event Handling
### Single Mode
`onChanged` fires with a normalized `DateTime` (midnight, no time component) when the user selects a date:
```dart
WDatePicker(
value: _date,
onChanged: (date) {
setState(() => _date = date);
_loadSchedule(date);
},
)
```
### Range Mode
`onRangeChanged` fires twice during a range selection — once on the first click (start only) and once on the second click (complete range):
```dart
WDatePicker(
mode: DatePickerMode.range,
range: _range,
onRangeChanged: (range) {
setState(() => _range = range);
if (range.isComplete) {
_calculateDuration(range.start, range.end!);
}
},
)
```
## State Variants
`WDatePicker` automatically manages several interactive states. Use state prefixes in `className` to apply conditional styles.
| State | Activated When |
|:------|:---------------|
| `hover:` | Mouse hovers over the trigger |
| `focus:` | Calendar popover is open |
| `open:` | Calendar popover is open (alias for `focus:`) |
| `disabled:` | `disabled` prop is `true` |
| `selected:` | A date or range has been selected |
```dart
WDatePicker(
value: _date,
onChanged: (date) => setState(() => _date = date),
className: 'p-3 border border-gray-300 rounded-lg '
'hover:border-blue-400 '
'focus:border-blue-500 focus:ring-2 focus:ring-blue-200 '
'selected:bg-blue-50 '
'disabled:opacity-50 disabled:bg-gray-100',
)
```
### Disabled State
When `disabled: true`, the trigger shows a forbidden cursor and the popover won't open:
```dart
WDatePicker(
disabled: true,
value: DateTime(2025, 6, 15),
className: 'p-3 border rounded-lg disabled:opacity-50 disabled:bg-gray-100',
)
```
## Styling Examples
### Default Styling
Without a `className`, the trigger uses built-in defaults with dark mode support:
```dart
WDatePicker(
value: _date,
onChanged: (date) => setState(() => _date = date),
// Uses: 'bg-white border border-gray-300 rounded-lg p-3
// dark:bg-gray-800 dark:border-gray-600'
)
```
### Interactive with Ring Focus
```dart
WDatePicker(
value: _date,
onChanged: (date) => setState(() => _date = date),
className: 'w-full p-3 bg-white dark:bg-gray-800 '
'border border-gray-300 dark:border-gray-600 rounded-lg '
'hover:border-blue-400 dark:hover:border-blue-500 '
'focus:border-blue-500 focus:ring-2 focus:ring-blue-200 '
'dark:focus:ring-blue-800 '
'selected:border-blue-500',
)
```
### Compact Inline
```dart
WDatePicker(
value: _date,
onChanged: (date) => setState(() => _date = date),
className: 'px-2 py-1 text-sm border rounded bg-gray-50 hover:bg-white',
placeholder: 'Date',
)
```
### Borderless with Shadow
```dart
WDatePicker(
value: _date,
onChanged: (date) => setState(() => _date = date),
className: 'p-3 bg-white rounded-xl shadow-md hover:shadow-lg',
)
```
## Calendar Internals
The calendar popover is styled with a fixed-width container:
```
'w-[320px] bg-white dark:bg-gray-800 border border-gray-200
dark:border-gray-700 rounded-xl shadow-xl p-4'
```
The calendar grid consists of:
| Component | Details |
|:----------|:--------|
| **Header** | Month/year label with left/right navigation arrows |
| **Weekday row** | `Mo Tu We Th Fr Sa Su` (Monday start) |
| **Date grid** | 6 rows × 7 columns = 42 cells |
| **Today** | Highlighted with `bg-gray-100 dark:bg-gray-700 rounded-full` |
| **Selected** | `bg-blue-500 text-white rounded-full` |
| **In range** | `bg-blue-100 dark:bg-blue-900/30 text-blue-700` |
| **Out of month** | `text-gray-300 dark:text-gray-600` |
| **Disabled** | `text-gray-300 dark:text-gray-600`, no click |
> [!NOTE]
> The calendar chrome (header, grid, day cells) uses hardcoded Wind classes and is not configurable via `className`. The `className` prop only controls the trigger element.
## All Supported Classes
### Trigger (className)
The `className` prop styles the trigger container. All Wind utility classes are supported:
| Category | Examples |
|:---------|:---------|
| Background | `bg-white`, `bg-gray-50`, `dark:bg-gray-800` |
| Border | `border`, `border-2`, `border-gray-300`, `rounded-lg`, `rounded-xl` |
| Padding | `p-3`, `px-4`, `py-2` |
| Sizing | `w-full`, `w-64`, `w-[300px]` |
| Ring | `ring-2`, `ring-blue-200`, `ring-offset-2` |
| Shadow | `shadow-sm`, `shadow-md`, `shadow-lg` |
| Typography | `text-sm` (affects placeholder/display text indirectly via icon color) |
| Opacity | `opacity-50`, `opacity-75` |
| State prefixes | `hover:`, `focus:`, `open:`, `disabled:`, `selected:`, `dark:` |
| Responsive | `sm:`, `md:`, `lg:`, `xl:`, `2xl:` |
### What className Does NOT Control
The calendar popover, header, weekday labels, and day cells use internal Wind classes that are not configurable via props.
## Customizing Theme
The calendar's selection colors (`bg-blue-500`, `bg-blue-100`) and neutral grays use Tailwind color scales from `WindThemeData`. Override them to change the visual appearance:
```dart
WindTheme(
data: WindThemeData(
colors: {
...WindThemeData.defaultColors,
'blue': {
100: Color(0xFFDBEAFE), // Range fill
500: Color(0xFF3B82F6), // Selected day
700: Color(0xFF1D4ED8), // Range text
900: Color(0xFF1E3A8A), // Dark mode range
},
},
),
child: MyApp(),
)
```
## Related Documentation
- [WFormDatePicker](./w-form-date-picker.md) - Form-integrated date picker with validation
- [WSelect](./w-select.md) - Dropdown selection component
- [WPopover](./w-popover.md) - The underlying overlay engine
- [WInput](./w-input.md) - Standard text input widget
---
# WFormCheckbox
# WFormCheckbox
A Wind-styled checkbox that integrates with Flutter's Form validation, providing built-in support for labels, hints, and error states.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WFormCheckbox(
value: _agreeTerms,
onChanged: (v) => setState(() => _agreeTerms = v),
labelText: 'I agree to Terms of Service',
className: 'w-5 h-5 rounded border checked:bg-blue-500 error:border-red-500',
validator: (value) => value != true ? 'You must agree to terms' : null,
)
```
## Basic Usage
The `WFormCheckbox` widget wraps a `WCheckbox` with a `FormField`. This allows you to use it directly inside a `Form` widget for easy validation and state management. When validation fails, it automatically triggers the `error:` state for its utility classes.
```dart
Form(
key: _formKey,
child: WFormCheckbox(
labelText: 'Newsletter',
hint: 'We will send you updates once a week.',
value: isSubscribed,
onChanged: (v) => setState(() => isSubscribed = v),
validator: (v) => v == false ? 'Required' : null,
),
)
```
## Constructor
```dart
WFormCheckbox({
Key? key,
bool value = false,
FormFieldValidator? validator,
FormFieldSetter? onSaved,
AutovalidateMode? autovalidateMode,
bool enabled = true,
ValueChanged? onChanged,
String? className,
String? iconClassName,
bool disabled = false,
IconData? checkIcon,
Set? states,
Widget? label,
String? labelText,
String labelClassName = 'text-sm text-gray-700',
String? hint,
String hintClassName = 'text-gray-500 text-xs mt-1',
bool showError = true,
String errorClassName = 'text-red-500 text-xs mt-1',
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `value` | `bool` | `false` | The current value of the checkbox. |
| `className` | `String?` | `null` | Wind utility classes for the checkbox container. |
| `onChanged` | `ValueChanged?` | `null` | Callback when the value changes. |
| `labelText` | `String?` | `null` | Simple text label displayed next to the checkbox. |
| `label` | `Widget?` | `null` | Custom label widget (takes precedence over `labelText`). |
| `labelClassName` | `String` | `'text-sm text-gray-700'` | Styling for the text label. |
| `hint` | `String?` | `null` | Optional hint text displayed below the checkbox. |
| `hintClassName` | `String` | `'text-gray-500 text-xs mt-1'` | Styling for the hint text. |
| `showError` | `bool` | `true` | Whether to display validation error messages. |
| `errorClassName` | `String` | `'text-red-500 text-xs mt-1'` | Styling for the error message. |
| `iconClassName` | `String?` | `null` | Utility classes for the check icon. |
| `checkIcon` | `IconData?` | `null` | Custom icon to use when checked. |
| `disabled` | `bool` | `false` | Disables the checkbox and its label interaction. |
| `states` | `Set?` | `null` | Custom state identifiers merged with built-in `checked` and `disabled` triggers. |
## Layout Modes
### Label & Hint Layout
`WFormCheckbox` automatically handles the layout of the checkbox, label, and hint/error text. It uses a vertical flex container where the checkbox and label are aligned horizontally, and the hint or error is positioned below them.
```dart
WFormCheckbox(
labelText: 'Receive notifications',
hint: 'Enable to get push notifications on your device.',
className: 'w-6 h-6',
)
```
## Event Handling
The `onChanged` callback is triggered whenever the user toggles the checkbox or clicks on the associated label. Since it's a `FormField`, it also integrates with standard form lifecycle events like `onSaved` and `validator`.
```dart
WFormCheckbox(
value: _val,
onChanged: (newValue) {
print('Checkbox is now: $newValue');
setState(() => _val = newValue);
},
onSaved: (v) => _model.termsAccepted = v ?? false,
)
```
## State Variants
`WFormCheckbox` supports all standard Wind state prefixes. Specifically, it activates the `error:` prefix when the form field validation fails.
```dart
WFormCheckbox(
// Base style is gray, turns blue when checked, red on error
className: 'border-gray-300 checked:bg-blue-500 error:border-red-500',
validator: (v) => v != true ? 'Required' : null,
)
```
## Styling Examples
### Custom Colors and Sizes
You can easily customize the appearance of the checkbox and its icon using standard Tailwind-like classes.
```dart
WFormCheckbox(
className: 'w-8 h-8 rounded-full border-2 border-indigo-200 checked:bg-indigo-600 checked:border-transparent',
iconClassName: 'text-white text-xl',
labelText: 'Large Indigo Checkbox',
labelClassName: 'text-lg font-bold text-indigo-900',
)
```
### Error State Customization
You can control how the error message appears when validation fails.
```dart
WFormCheckbox(
labelText: 'Agree to terms',
errorClassName: 'text-orange-600 italic font-medium mt-2',
validator: (v) => v != true ? 'This field is mandatory!' : null,
)
```
## All Supported Classes
| Category | Classes |
|:---------|:--------|
| Layout | `flex`, `flex-col`, `items-start`, `gap-{n}` |
| Spacing | `p-{n}`, `m-{n}`, `pt-{n}`, `pl-{n}` |
| Sizing | `w-{size}`, `h-{size}`, `aspect-square` |
| Typography | `text-{color}`, `text-{size}`, `font-{weight}`, `italic` |
| Colors | `bg-{color}`, `border-{color}`, `checked:bg-{color}` |
| Borders | `border`, `border-{width}`, `rounded`, `rounded-{size}` |
| Form | `error:{utility}`, `disabled:{utility}` |
## Customizing Theme
You can define default styles for form components in your `WindThemeData`.
```dart
WindThemeData(
// While WFormCheckbox doesn't have a specific theme object,
// it respects global color and spacing scales.
colors: {
'primary': Colors.blue,
'danger': Colors.red,
},
baseSpacingUnit: 4.0,
)
```
## Related Documentation
- [WCheckbox](./w-checkbox.md) - The base checkbox component.
- [WFormInput](./w-form-input.md) - Form-integrated text input.
- [WAnchor](./w-anchor.md) - The wrapper used for label interactivity.
- [WDiv](./w-div.md) - The container used for layout.
---
# WFormInput
# WFormInput
A Wind-styled input that integrates seamlessly with Flutter's native Form validation system. It extends `FormField` to provide automatic error handling, label management, and state-based styling through utility classes.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WFormInput(
label: 'Email Address',
placeholder: 'you@example.com',
className: 'p-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 error:border-red-500',
validator: (value) {
if (value == null || value.isEmpty) return 'Email is required';
if (!value.contains('@')) return 'Invalid email address';
return null;
},
)
```
## Basic Usage
The `WFormInput` widget wraps a standard `WInput` with a `FormField`. It automatically manages its own internal `TextEditingController` if one isn't provided, and synchronizes its state with the parent `Form` widget.
When validation fails, the widget automatically activates the `error` state, enabling any `error:` prefixed utility classes in your `className`.
```dart
Form(
key: _formKey,
child: Column(
children: [
WFormInput(
label: 'Username',
className: 'border p-2 error:bg-red-50',
validator: (v) => v!.length < 3 ? 'Too short' : null,
),
WButton(
onTap: () => _formKey.currentState!.validate(),
child: Text('Submit'),
),
],
),
)
```
## Constructor
```dart
WFormInput({
Key? key,
TextEditingController? controller,
String? initialValue,
String? Function(String?)? validator,
void Function(String?)? onSaved,
AutovalidateMode? autovalidateMode,
bool enabled = true,
// WInput props
InputType type = InputType.text,
String? placeholder,
String? className,
String? placeholderClassName,
TextInputAction? textInputAction,
ValueChanged? onSubmitted,
ValueChanged? onChanged,
VoidCallback? onEditingComplete,
VoidCallback? onTap,
TapRegionCallback? onTapOutside,
bool readOnly = false,
bool autofocus = false,
int? maxLines,
int minLines = 1,
Set? states,
List? inputFormatters,
TextCapitalization textCapitalization = TextCapitalization.none,
bool autocorrect = true,
bool enableSuggestions = true,
// Layout props
String? label,
String labelClassName = 'text-sm font-medium text-gray-700 mb-1',
String? hint,
String hintClassName = 'text-gray-500 text-xs mt-1',
bool showError = true,
String errorClassName = 'text-red-500 text-xs mt-1',
// Prefix/Suffix
Widget? prefix,
Widget? suffix,
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `className` | `String?` | `null` | Utility classes for the input field. Supports `error:`, `focus:`, and `disabled:` prefixes. |
| `label` | `String?` | `null` | Label text displayed above the input. |
| `labelClassName` | `String` | `'text-sm...'` | Styling for the label text. |
| `hint` | `String?` | `null` | Helper text displayed below the input. Hidden when validation error is active. |
| `hintClassName` | `String` | `'text-gray-500...'` | Styling for the hint text. |
| `showError` | `bool` | `true` | Whether to display the error message string below the input. |
| `errorClassName` | `String` | `'text-red-500...'` | Styling for the validation error message text. |
| `controller` | `TextEditingController?` | `null` | Optional external controller for manual text management. |
| `validator` | `String? Function(String?)?` | `null` | Form validation logic returning error string or null. |
| `type` | `InputType` | `InputType.text` | Determines keyboard layout and visual masking (e.g., `password`). |
| `prefix` | `Widget?` | `null` | Widget (like an Icon) displayed before the input text. |
| `suffix` | `Widget?` | `null` | Widget (like a visibility toggle) displayed after the input text. |
## Layout Modes
### Vertical Stack (Default)
`WFormInput` organizes its components in a vertical column: label at the top, followed by the input field, and then the hint or error message at the bottom.
```dart
WFormInput(
label: 'Account Number',
hint: 'Enter the 12-digit number found on your statement.',
className: 'border p-3 rounded',
)
```
## Event Handling
`WFormInput` supports all standard text input events. `onChanged` automatically notifies the internal `FormFieldState` to track validation in real-time if `autovalidateMode` is enabled.
```dart
WFormInput(
className: 'border p-2',
onChanged: (value) => print('Typing: $value'),
onSubmitted: (value) => print('Final value: $value'),
)
```
## State Variants
Interactive styling is achieved through state prefixes. `WFormInput` automatically manages the `error` state based on validation results.
```dart
WFormInput(
className: '''
border border-gray-300
focus:border-blue-500 focus:ring-1
error:border-red-500 error:bg-red-50
disabled:bg-gray-100 disabled:text-gray-400
''',
validator: (v) => v!.isEmpty ? 'Required' : null,
)
```
## Styling Examples
### Custom Error Feedback
You can completely customize how the error appears by using `error:` classes on the main input and styling the error message specifically.
```dart
WFormInput(
label: 'Password',
type: InputType.password,
className: 'border-b-2 error:border-red-600',
errorClassName: 'text-red-600 font-bold mt-2 italic',
validator: (v) => v!.length < 8 ? 'Password too short' : null,
)
```
### Minimalist Style
Removing the default labels and hints for a tighter, cleaner UI.
```dart
WFormInput(
placeholder: 'Search...',
className: 'bg-gray-100 rounded-full px-4 py-2 border-none focus:bg-white focus:ring-2 focus:ring-blue-500/50',
showError: false, // Only use error: styling on the input itself
)
```
## All Supported Classes
Since `WFormInput` uses `WInput` internally, it supports all standard utility categories.
| Category | Classes |
|:---------|:--------|
| Layout | `flex`, `grid`, `block`, `hidden` |
| Spacing | `p-{n}`, `m-{n}`, `gap-{n}` |
| Sizing | `w-{size}`, `h-{size}` |
| Typography | `text-{size}`, `font-{weight}` |
| Colors | `bg-{color}`, `text-{color}` |
| Borders | `border`, `rounded`, `ring` |
| State | `hover:`, `focus:`, `disabled:`, `error:` |
## Customizing Theme
Default styles for labels, hints, and error messages can be influenced globally through `WindThemeData`.
```dart
WindThemeData(
// The base spacing unit affects all p-{n}, m-{n}, and gap-{n} classes
baseSpacingUnit: 4.0,
)
```
## Related Documentation
- [WInput](./w-input.md) - The base input widget.
- [WButton](./w-button.md) - Standard button for form submission.
- [WFormCheckbox](./w-form-checkbox.md) - Form-integrated checkbox.
- [WText](./w-text.md) - The underlying typography system.
---
# WFormSelect
# WFormSelect
A Wind-styled single or multi-select dropdown that integrates seamlessly with Flutter's Form validation. It wraps the core `WSelect` widget with `FormField` logic, providing labels, hint text, and automatic error state handling.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Layout Modes](#layout-modes)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WFormSelect(
label: 'Country',
placeholder: 'Select your country',
options: [
SelectOption(value: 'us', label: 'United States'),
SelectOption(value: 'uk', label: 'United Kingdom'),
],
className: 'w-full border rounded-lg error:border-red-500',
validator: (value) => value == null ? 'Please select a country' : null,
)
```
## Basic Usage
The `WFormSelect` (and its counterpart `WFormMultiSelect`) is designed to be used inside a `Form` widget. It automatically manages its own state and displays validation errors using the `error:` utility prefix when validation fails.
```dart
Form(
key: _formKey,
child: Column(
children: [
WFormSelect(
value: _selectedValue,
options: myOptions,
onChange: (v) => setState(() => _selectedValue = v),
label: 'Select Category',
validator: (v) => v == null ? 'Required' : null,
),
WButton(
onTap: () {
if (_formKey.currentState!.validate()) {
// Process form
}
},
child: WText('Submit'),
),
],
),
)
```
## Constructor
### WFormSelect
```dart
WFormSelect({
Key? key,
T? value,
String? Function(T?)? validator,
void Function(T?)? onSaved,
AutovalidateMode? autovalidateMode,
bool enabled = true,
required List> options,
ValueChanged? onChange,
String placeholder = 'Select an option',
String? className,
String? menuClassName,
bool searchable = false,
String? label,
String? hint,
bool showError = true,
// ... other WSelect props
})
```
### WFormMultiSelect
```dart
WFormMultiSelect({
Key? key,
List? values,
String? Function(List?)? validator,
void Function(List?)? onSaved,
required List> options,
ValueChanged>? onMultiChange,
String placeholder = 'Select options',
String? label,
String? hint,
// ... other multi-select props
})
```
## Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `className` | `String?` | `null` | Wind utility classes for the select trigger. |
| `options` | `List>` | **Required** | The list of available options to display. |
| `value` | `T?` | `null` | The currently selected value (single select). |
| `values` | `List?` | `[]` | The currently selected values (multi select). |
| `label` | `String?` | `null` | Optional label text displayed above the select. |
| `hint` | `String?` | `null` | Optional hint text displayed below the select. |
| `validator` | `String? Function(T?)?` | `null` | Form validation logic. |
| `showError` | `bool` | `true` | Whether to display the error message when invalid. |
| `searchable` | `bool` | `false` | Enables a search input inside the dropdown. |
| `disabled` | `bool` | `false` | Disables user interaction. |
| `labelClassName`| `String` | `text-sm font-medium...` | Styling for the label text. |
| `errorClassName`| `String` | `text-red-500 text-xs...` | Styling for the error message. |
## Layout Modes
`WFormSelect` always uses a vertical `flex-col` layout to stack the label, the select trigger, and the error/hint text.
### Standard Stack
By default, the widget arranges elements in a clean vertical flow.
```dart
WFormSelect(
label: 'Department',
hint: 'Choose your primary department',
options: departments,
className: 'border p-3 rounded-md',
)
```
## Event Handling
`WFormSelect` provides standard Form event handlers alongside the select-specific change callbacks.
```dart
WFormSelect(
options: options,
onChange: (value) {
print('Selected: $value');
},
onSaved: (value) {
// Called when FormState.save() is invoked
_formData.dept = value;
},
validator: (value) {
if (value == null) return 'Selection required';
return null;
},
)
```
## State Variants
Since `WFormSelect` injects the `error` state into the underlying `WSelect`, you can use the `error:` prefix to change the styling when validation fails.
```dart
WFormSelect(
className: 'border-gray-300 focus:border-blue-500 error:border-red-500 error:bg-red-50',
options: options,
validator: (v) => v == null ? 'Error' : null,
)
```
## Styling Examples
### Multi-Select Form Field
The `WFormMultiSelect` variation handles lists of values and provides a different set of styling options for selected chips.
```dart
WFormMultiSelect(
label: 'Interests',
values: _selectedInterests,
options: interestOptions,
className: 'border-2 rounded-xl p-2',
onMultiChange: (vals) => setState(() => _selectedInterests = vals),
validator: (vals) => vals!.isEmpty ? 'Select at least one' : null,
)
```
### Searchable Form Select
Perfect for long lists of options where users need to filter quickly.
```dart
WFormSelect(
label: 'User',
searchable: true,
searchPlaceholder: 'Search by name...',
options: users,
className: 'w-full px-4 py-3 bg-white border',
)
```
## All Supported Classes
Since `WFormSelect` wraps `WSelect`, it supports all standard Wind utilities on the `className` and `menuClassName` props.
| Category | Classes |
|:---------|:--------|
| Sizing | `w-full`, `max-w-md`, `h-12` |
| Spacing | `p-{n}`, `px-{n}`, `py-{n}`, `m-{n}` |
| Borders | `border`, `rounded-{size}`, `ring-{n}` |
| Colors | `bg-{color}`, `text-{color}`, `border-{color}` |
| States | `hover:`, `focus:`, `disabled:`, `error:` |
## Customizing Theme
You can globally customize the default styling of form labels and error messages through `WindThemeData`.
```dart
WindThemeData(
// Customizing default form select behavior via theme defaults
)
```
## Related Documentation
- [WSelect - Core Dropdown Widget](./w-select.md)
- [WFormInput - Validated Text Input](./w-form-input.md)
- [WFormCheckbox - Validated Checkbox](./w-form-checkbox.md)
- [Forms in Wind](../core-concepts/forms.md)
---
# WFormDatePicker
# WFormDatePicker
A form-integrated date picker that wraps [WDatePicker](./w-date-picker.md) with Flutter's `FormField`. Provides native form validation, automatic `error:` state styling, and optional label, hint, and error message display.
- [Basic Usage](#basic-usage)
- [Constructor](#constructor)
- [Props](#props)
- [Form Validation](#form-validation)
- [Range Mode in Forms](#range-mode-in-forms)
- [Event Handling](#event-handling)
- [State Variants](#state-variants)
- [Styling Examples](#styling-examples)
- [All Supported Classes](#all-supported-classes)
- [Customizing Theme](#customizing-theme)
- [Related Documentation](#related-documentation)
```dart
WFormDatePicker(
label: 'Birth Date',
hint: 'We need your date of birth for verification',
className: 'p-3 border border-gray-300 rounded-lg error:border-red-500',
validator: (value) => value == null ? 'Please select a date' : null,
onChanged: (date) => print('Selected: $date'),
)
```
## Basic Usage
`WFormDatePicker` works inside a Flutter `Form` widget. It renders a vertical layout with an optional label above the date picker and error/hint text below.
```dart
final _formKey = GlobalKey();
Form(
key: _formKey,
child: Column(
children: [
WFormDatePicker(
label: 'Event Date',
className: 'p-3 border rounded-lg error:border-red-500',
validator: (date) => date == null ? 'Date is required' : null,
onChanged: (date) => _eventDate = date,
),
WButton(
className: 'mt-4 bg-blue-500 text-white px-4 py-2 rounded-lg',
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
}
},
child: WText('Submit'),
),
],
),
)
```
When validation fails, the widget automatically adds the `error` state to the inner `WDatePicker`, activating any `error:` prefixed classes.
## Constructor
```dart
WFormDatePicker({
Key? key,
// FormField params
FormFieldValidator? validator,
FormFieldSetter? onSaved,
AutovalidateMode? autovalidateMode,
bool enabled = true,
// WDatePicker params
DateTime? initialValue,
DateRange? initialRange,
DatePickerMode mode = DatePickerMode.single,
ValueChanged? onChanged,
ValueChanged? onRangeChanged,
DateTime? minDate,
DateTime? maxDate,
String? className,
String? placeholder,
Set? states,
DateDisplayFormat? displayFormat,
// Form wrapper params
String? label,
String labelClassName = 'text-sm font-medium text-gray-700 dark:text-gray-300 mb-1',
bool showError = true,
String errorClassName = 'text-red-500 text-xs mt-1',
String? hint,
String hintClassName = 'text-gray-500 text-xs mt-1',
})
```
## Props
### FormField Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `validator` | `FormFieldValidator?` | `null` | Validation function, receives `DateTime?` |
| `onSaved` | `FormFieldSetter?` | `null` | Called when form is saved |
| `autovalidateMode` | `AutovalidateMode?` | `null` | When to auto-validate |
| `enabled` | `bool` | `true` | Whether the picker is interactive |
| `initialValue` | `DateTime?` | `null` | Initial selected date (single mode) |
### Date Picker Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `mode` | `DatePickerMode` | `single` | Selection mode: `single` or `range` |
| `initialRange` | `DateRange?` | `null` | Initial date range (range mode) |
| `onChanged` | `ValueChanged?` | `null` | Called on date selection (single mode) |
| `onRangeChanged` | `ValueChanged?` | `null` | Called on range selection (range mode) |
| `minDate` | `DateTime?` | `null` | Earliest selectable date |
| `maxDate` | `DateTime?` | `null` | Latest selectable date |
| `className` | `String?` | `null` | Wind utility classes for the trigger |
| `placeholder` | `String?` | `null` | Placeholder text (defaults to `'Select date'`) |
| `states` | `Set?` | `null` | Custom states for dynamic styling |
| `displayFormat` | `DateDisplayFormat?` | `null` | Custom date display format function |
### Layout Props
| Prop | Type | Default | Description |
|:-----|:-----|:--------|:------------|
| `label` | `String?` | `null` | Label text displayed above the picker |
| `labelClassName` | `String` | `'text-sm font-medium text-gray-700 dark:text-gray-300 mb-1'` | Wind classes for the label |
| `showError` | `bool` | `true` | Whether to display validation error text |
| `errorClassName` | `String` | `'text-red-500 text-xs mt-1'` | Wind classes for error message |
| `hint` | `String?` | `null` | Hint text displayed below the picker |
| `hintClassName` | `String` | `'text-gray-500 text-xs mt-1'` | Wind classes for hint text |
> [!NOTE]
> When both an error and hint are present, the error message takes priority and the hint is hidden.
## Form Validation
The `validator` function receives the currently selected `DateTime?` value. Return a `String` to show an error, or `null` if valid.
```dart
WFormDatePicker(
label: 'Departure Date',
className: 'p-3 border rounded-lg error:border-red-500 error:bg-red-50',
validator: (date) {
if (date == null) return 'Please select a departure date';
if (date.isBefore(DateTime.now())) return 'Date must be in the future';
return null;
},
)
```
### Auto-Validation
Use `autovalidateMode` to validate as the user interacts:
```dart
WFormDatePicker(
label: 'Start Date',
autovalidateMode: AutovalidateMode.onUserInteraction,
className: 'p-3 border rounded-lg error:border-red-500',
validator: (date) => date == null ? 'Required' : null,
)
```
### Using onSaved
```dart
DateTime? _savedDate;
WFormDatePicker(
label: 'Meeting Date',
className: 'p-3 border rounded-lg',
onSaved: (date) => _savedDate = date,
validator: (date) => date == null ? 'Required' : null,
)
```
## Range Mode in Forms
In range mode, the `FormField` internally tracks the range's **start date** for validation purposes. This means a simple null check still works as a "required" validator.
```dart
WFormDatePicker(
label: 'Trip Dates',
mode: DatePickerMode.range,
initialRange: null,
className: 'p-3 border rounded-lg error:border-red-500',
placeholder: 'Select check-in / check-out',
validator: (date) => date == null ? 'Please select your trip dates' : null,
onRangeChanged: (range) {
if (range.isComplete) {
_calculateTripDuration(range);
}
},
)
```
> [!NOTE]
> The `validator` receives the range's start `DateTime` — not a `DateRange` object. For more complex range validation (e.g., minimum stay duration), use the `onRangeChanged` callback to manage validation externally.
## Event Handling
`onChanged` and `onRangeChanged` fire alongside the form state updates. The `FormFieldState` is updated automatically — you don't need to call `didChange` yourself.
```dart
WFormDatePicker(
label: 'Appointment',
onChanged: (date) {
// FormField state is already updated at this point
_loadAvailableSlots(date);
},
)
```
## State Variants
`WFormDatePicker` inherits all state variants from `WDatePicker` and adds the `error` state automatically when validation fails.
| State | Activated When |
|:------|:---------------|
| `hover:` | Mouse hovers over the trigger |
| `focus:` | Calendar popover is open |
| `open:` | Calendar popover is open (alias) |
| `disabled:` | `enabled` is `false` |
| `selected:` | A date or range has been selected |
| `error:` | Form validation has failed |
```dart
WFormDatePicker(
label: 'Date',
className: 'p-3 border border-gray-300 rounded-lg '
'hover:border-blue-400 '
'focus:border-blue-500 focus:ring-2 focus:ring-blue-200 '
'error:border-red-500 error:ring-2 error:ring-red-200 '
'disabled:opacity-50 disabled:bg-gray-100',
validator: (date) => date == null ? 'Required' : null,
)
```
## Styling Examples
### Standard Form Field
```dart
WFormDatePicker(
label: 'Date of Birth',
hint: 'You must be at least 18 years old',
className: 'w-full p-3 border border-gray-300 rounded-lg '
'focus:border-blue-500 focus:ring-2 focus:ring-blue-200 '
'error:border-red-500',
maxDate: DateTime.now().subtract(const Duration(days: 365 * 18)),
validator: (date) => date == null ? 'Date of birth is required' : null,
)
```
### Dark Mode Ready
```dart
WFormDatePicker(
label: 'Schedule',
labelClassName: 'text-sm font-medium text-gray-700 dark:text-gray-300',
className: 'p-3 bg-white dark:bg-gray-800 '
'border border-gray-300 dark:border-gray-600 rounded-lg '
'hover:border-blue-400 dark:hover:border-blue-500 '
'error:border-red-500 dark:error:border-red-400',
errorClassName: 'text-red-500 dark:text-red-400 text-xs mt-1',
validator: (date) => date == null ? 'Required' : null,
)
```
### Minimalist
```dart
WFormDatePicker(
label: 'When',
labelClassName: 'text-xs text-gray-500 uppercase tracking-wider',
className: 'border-b border-gray-200 py-2 rounded-none '
'focus:border-blue-500 error:border-red-500',
hintClassName: 'text-gray-400 text-xs mt-0.5',
hint: 'Optional',
)
```
## All Supported Classes
### Trigger (className)
All Wind utility classes work on the trigger container. The most commonly used:
| Category | Examples |
|:---------|:---------|
| Background | `bg-white`, `bg-gray-50`, `dark:bg-gray-800` |
| Border | `border`, `border-gray-300`, `rounded-lg` |
| Padding | `p-3`, `px-4`, `py-2` |
| Sizing | `w-full`, `w-64` |
| Ring | `ring-2`, `ring-blue-200` |
| Shadow | `shadow-sm` |
| State prefixes | `hover:`, `focus:`, `disabled:`, `selected:`, `error:`, `dark:` |
### Label (labelClassName)
Default: `text-sm font-medium text-gray-700 dark:text-gray-300 mb-1`
### Error (errorClassName)
Default: `text-red-500 text-xs mt-1`
### Hint (hintClassName)
Default: `text-gray-500 text-xs mt-1`
## Customizing Theme
`WFormDatePicker` inherits all theme customization from `WDatePicker`. Override Tailwind color scales in `WindThemeData` to change selection colors, error states, and calendar chrome:
```dart
WindTheme(
data: WindThemeData(
colors: {
...WindThemeData.defaultColors,
'red': {
500: Color(0xFFEF4444), // Error border color
200: Color(0xFFFECACA), // Error ring color
},
},
),
child: MyApp(),
)
```
## Related Documentation
- [WDatePicker](./w-date-picker.md) - The base date picker widget
- [WFormInput](./w-form-input.md) - Form-integrated text input
- [WFormSelect](./w-form-select.md) - Form-integrated dropdown
- [WFormCheckbox](./w-form-checkbox.md) - Form-integrated checkbox
- [WPopover](./w-popover.md) - The underlying overlay engine
---