-
Notifications
You must be signed in to change notification settings - Fork 0
feat: CSS positioning utilities — relative/absolute/fixed/sticky + inset offsets #49
Copy link
Copy link
Closed
Description
Problem
Wind UI has no parser for CSS positioning utilities. Developers are forced to use Flutter native Stack/Positioned widgets for any positioned layout, breaking the Wind UI-first philosophy.
Current support audit
| Feature | Supported? | Parser |
|---|---|---|
relative, absolute, fixed, sticky |
❌ NO | None |
top-0, bottom-4, left-2, right-8 |
❌ NO | None |
inset-0, inset-x-0, inset-y-0 |
❌ NO | None |
z-10, z-20, z-50 |
✅ YES | ZIndexParser |
overflow-hidden, overflow-y-auto |
✅ YES | OverflowParser |
Consumer impact
In the Kodizm Flutter app, every positioned layout falls back to native widgets:
// CURRENT — forced to use native Stack/Positioned
Stack(
children: [
_buildMessageArea(),
Positioned(
bottom: 16,
right: 16,
child: _buildScrollToBottomFAB(),
),
],
)// DESIRED — pure Wind UI
WDiv(
className: 'relative',
children: [
_buildMessageArea(),
WDiv(
className: 'absolute bottom-4 right-4',
child: _buildScrollToBottomFAB(),
),
],
)Common use cases blocked
- Badge overlays: Count badge at top-right of an icon
- FAB positioning: Floating action buttons pinned to corners
- Overlay panels: Dropdown menus, tooltips, popovers
- Sticky headers: Fixed headers within scroll containers
- Toast/notification positioning: Fixed-position notifications
Proposed Solution
1. Create PositionParser
Add a new parser at lib/src/parser/parsers/position_parser.dart that handles:
Position type tokens:
relative→ Sets_isRelative: trueon WindStyle (renders asStack)absolute→ Sets_isAbsolute: trueon WindStyle (renders asPositionedinside parentStack)fixed→ Sets_isFixed: trueon WindStyle (renders as overlay orPositioned.fill)sticky→ Sets_isSticky: trueon WindStyle (considerSliverPersistentHeaderor skip for v1)
Offset tokens (Tailwind convention):
top-{n},bottom-{n},left-{n},right-{n}— pixel offsets using the spacing scaletop-0,bottom-0, etc. — zero offsetinset-{n}→ all four sidesinset-x-{n}→ left + rightinset-y-{n}→ top + bottom- Arbitrary values:
top-[12px],left-[50%]
2. Extend WindStyle
Add positioning fields to WindStyle in lib/src/parser/wind_style.dart:
class WindStyle {
// ... existing fields ...
// Positioning
PositionType? position; // relative, absolute, fixed, sticky
double? top;
double? right;
double? bottom;
double? left;
}
enum PositionType { relative, absolute, fixed, sticky }3. Update Widget Rendering
In the WDiv build logic, when position is set:
relative→ Wraps children in aStackabsolute→ Wraps self inPositioned(top:, right:, bottom:, left:)— expects parent to be aStack(fromrelative)fixed→ UsesOverlayorPositioned.filldepending on context
Flutter mapping
| Wind UI className | Flutter Widget |
|---|---|
relative (parent) |
Stack(children: [...]) |
absolute top-0 right-0 (child) |
Positioned(top: 0, right: 0, child: ...) |
absolute inset-0 |
Positioned.fill(child: ...) |
absolute bottom-4 right-4 |
Positioned(bottom: 16, right: 16, child: ...) |
Spacing scale for offsets
Follow the existing Wind UI spacing scale (4px base):
| Token | Value |
|---|---|
top-0 |
0px |
top-1 |
4px |
top-2 |
8px |
top-3 |
12px |
top-4 |
16px |
top-5 |
20px |
top-6 |
24px |
top-8 |
32px |
top-10 |
40px |
top-12 |
48px |
top-16 |
64px |
top-[24px] |
24px (arbitrary) |
Files to Create/Modify
| File | Action |
|---|---|
lib/src/parser/parsers/position_parser.dart |
Create — new parser for position/offset tokens |
lib/src/parser/wind_style.dart |
Modify — add position, top, right, bottom, left fields |
lib/src/parser/wind_parser.dart |
Modify — register PositionParser in parser list |
lib/src/widgets/w_div.dart |
Modify — render Stack/Positioned based on position style |
test/parser/position_parser_test.dart |
Create — unit tests for all position tokens |
test/widget/position_test.dart |
Create — widget tests for Stack/Positioned rendering |
Acceptance Criteria
-
relativeclassName on WDiv renders its children inside aStack -
absoluteclassName on WDiv renders asPositionedinside parent Stack -
top-{n},bottom-{n},left-{n},right-{n}set Positioned offsets -
inset-{n}sets all four offsets,inset-x-{n}sets left+right,inset-y-{n}sets top+bottom - Arbitrary values work:
top-[24px],left-[50%] -
z-{n}(existing) works correctly with the new positioning system - Spacing scale matches Tailwind conventions (4px base unit)
- Tests cover: basic positioning, all offset directions, inset shortcuts, arbitrary values, z-index interaction
-
fixedandstickycan be deferred to a follow-up if complex — document as future work
Priority
This is the single biggest feature gap in Wind UI that forces Flutter native fallbacks. Every consumer app with overlays, badges, FABs, or tooltips hits this limitation.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels