Skip to content

feat: CSS positioning utilities — relative/absolute/fixed/sticky + inset offsets #49

@anilcancakir

Description

@anilcancakir

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: true on WindStyle (renders as Stack)
  • absolute → Sets _isAbsolute: true on WindStyle (renders as Positioned inside parent Stack)
  • fixed → Sets _isFixed: true on WindStyle (renders as overlay or Positioned.fill)
  • sticky → Sets _isSticky: true on WindStyle (consider SliverPersistentHeader or skip for v1)

Offset tokens (Tailwind convention):

  • top-{n}, bottom-{n}, left-{n}, right-{n} — pixel offsets using the spacing scale
  • top-0, bottom-0, etc. — zero offset
  • inset-{n} → all four sides
  • inset-x-{n} → left + right
  • inset-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 a Stack
  • absolute → Wraps self in Positioned(top:, right:, bottom:, left:) — expects parent to be a Stack (from relative)
  • fixed → Uses Overlay or Positioned.fill depending 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

  • relative className on WDiv renders its children inside a Stack
  • absolute className on WDiv renders as Positioned inside 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
  • fixed and sticky can 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions