Customization

Every Duxt UI component supports className overrides, HTML attributes, and event handlers for full control.

Class Overrides with className

All components accept a className prop. When you pass Tailwind classes, they intelligently replace conflicting base classes using twMerge. For example, passing className: 'bg-purple-500' to a primary button replaces bg-cyan-500 instead of appending both.

dart
1
2
3
4
5
6
7
8
9
10
11
// Default button
DButton(label: 'Default', color: DButtonColor.primary)

// Override background color - replaces bg-cyan-500
DButton(label: 'Purple BG', className: 'bg-purple-500 hover:bg-purple-600')

// Override border radius
DButton(label: 'Rounded Full', className: 'rounded-full')

// Add extra classes (non-conflicting classes are appended)
DButton(label: 'Large Shadow', className: 'shadow-xl')

Smart Class Merging

The twMerge utility resolves conflicts between these Tailwind utility groups:

Group Prefix Example
Background bg-* bg-red-500 replaces bg-cyan-500
Text Color text-{color} text-white replaces text-gray-700
Text Size text-{size} text-lg replaces text-sm
Font Weight font-* font-bold replaces font-medium
Border Radius rounded* rounded-full replaces rounded-md
Border Color border-{color} border-red-500 replaces border-gray-300
Ring ring* ring-2 replaces ring
Shadow shadow* shadow-xl replaces shadow-md
Padding p-*, px-*, py-* p-8 replaces p-4
Margin m-*, mx-*, my-* mt-4 replaces mt-2
Width/Height w-*, h-* w-full replaces w-auto
Display flex, block, etc. block replaces inline-flex

Classes that do not conflict are always kept. Variant prefixes like dark:, hover:, focus: are handled correctly and only conflict within the same variant.

Works on All Components

Custom

Red border alert

Custom

Card with cyan border

dart
1
2
3
4
5
6
7
8
// Alert with custom border
DAlert(title: 'Custom', description: 'Red border', className: 'border-red-500')

// Badge with custom colors
DBadge(label: 'Custom', className: 'bg-purple-500 text-white')

// Card with custom border
DCard(className: 'border-cyan-500', children: [...])

HTML id

Pass id to set the HTML id attribute on the root element. Useful for anchors, JavaScript integration, and testing.

dart
1
2
3
DButton(label: 'Submit', id: 'submit-btn')
DInput(placeholder: 'Email', id: 'email-input')
DModal(id: 'settings-modal', trigger: DButton(label: 'Settings'), children: [...])

HTML Attributes

Pass any HTML attribute via the attributes map. These are applied to the root element. Components that have internal attributes (like ARIA labels) merge them so your values take precedence.

dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Data attributes for testing
DButton(
  label: 'Delete',
  attributes: {'data-testid': 'delete-btn', 'data-action': 'delete'},
)

// ARIA attributes
DInput(
  placeholder: 'Search...',
  attributes: {'aria-label': 'Search products', 'role': 'searchbox'},
)

// Custom data attributes
DCard(
  attributes: {'data-card-id': '42', 'data-category': 'featured'},
  children: [...],
)

Event Handlers

Add custom event handlers via the events map. Any DOM event is supported. These are applied to the root element alongside any built-in handlers.

dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Mouse events
DButton(
  label: 'Hover me',
  events: {
    'mouseenter': (e) => showTooltip(),
    'mouseleave': (e) => hideTooltip(),
  },
)

// Keyboard events
DInput(
  placeholder: 'Press Enter...',
  events: {
    'keydown': (e) => handleKeydown(e),
  },
)

// Focus events
DCard(
  events: {
    'focusin': (e) => highlight(),
    'focusout': (e) => unhighlight(),
  },
  children: [...],
)

Using twMerge Directly

The twMerge and mergeAttributes utilities are exported from duxt_ui and can be used in your own components:

dart
1
2
3
4
5
6
7
8
9
10
11
12
13
import 'package:duxt_ui/duxt_ui.dart';

// Smart class merging - conflicting classes are replaced
twMerge('bg-cyan-500 text-white rounded-md p-4', 'bg-red-500 rounded-full')
// => 'text-white p-4 bg-red-500 rounded-full'

// Non-conflicting classes are appended
twMerge('flex items-center gap-2', 'shadow-lg border')
// => 'flex items-center gap-2 shadow-lg border'

// Merge HTML attribute maps
mergeAttributes({'data-modal': 'true'}, {'data-testid': 'my-modal'})
// => {'data-modal': 'true', 'data-testid': 'my-modal'}