Skip to content

Gs/transition by property model (ClasshSS 2.0)#2

Open
augyg wants to merge 30 commits into
masterfrom
gs/transition-by-property-model
Open

Gs/transition by property model (ClasshSS 2.0)#2
augyg wants to merge 30 commits into
masterfrom
gs/transition-by-property-model

Conversation

@augyg

@augyg augyg commented Feb 19, 2026

Copy link
Copy Markdown
Owner

Summary

THIS IS A BREAKING CHANGE. This will be Classh2.0

This PR introduces type-safe, per-attribute Tailwind transitions and significantly expands the styling system with transitions, transforms, gradients, opacity, and improved type inference across the entire library.

The core goal: move from global transitions to per-value transitions while keeping the API ergonomic and backwards compatible.


Major Features

1) Per-attribute transitions (core feature)

Introduced a new WithTransition system that enables transitions to be attached directly to individual property values.

Key capabilities

  • Transitions now apply per property instead of globally

  • Works across responsive breakpoints and pseudo-classes (hover, focus, etc.)

  • Uses Tailwind arbitrary syntax:

    hover:[transition:background-color_300ms_in-out_0ms]
    

New API

  • WithTransition wrapper type

  • Builder helpers:

    • withTransition
    • withTiming
    • withDelay
    • withTransitionAll
  • Centralized helpers:

    • renderWithTransitionTW
    • compileWithTransitionTW

Global _transition config was removed in favor of fully per-attribute transitions.


2) Library-wide migration to transition-aware properties

Transitions are now supported across most styling domains:

Updated modules

  • Background (color, opacity, shadow)
  • Borders (color, width, radius)
  • Spacing (padding, margin)
  • Sizing (width/height/min/max)
  • Rings
  • Transforms

All relevant fields now use:

WhenTW (WithTransition a)

3) Improved type inference for setters

Replaced the temporary AutoWrap approach with a robust typeclass system:

New typeclasses

  • SetConstant
  • SetResponsive
  • AddResponsive
  • UnwrapType type family

Benefits:

  • Bare values automatically wrapped with noTransition
  • Expressions like h .~~ pct 100 now infer correctly
  • No extra type annotations required
  • Cleaner overlapping instance strategy (removed INCOHERENT)

4) Full Tailwind v3 transform support

Added a complete transform system:

Supported transforms

  • Rotate
  • Scale
  • Translate (X/Y)
  • Skew (X/Y)
  • Transform origin

All transforms support transitions and responsive/state variants.


5) Type-safe gradient system

Reworked background colors to support gradients:

New types

  • GradientColor

  • GradientDirection

  • ColorStop

  • Gradient helpers:

    • solidColor
    • linearGradient
    • linearGradientVia
    • positioned stops

Breaking changes

bgColor .~~ blue

becomes:

bgColor .~~ solidColor (Blue C500)

6) Unified color opacity system

Introduced ColorWithOpacity as the core color representation.

Supports Tailwind /opacity syntax everywhere:

bg-blue-500/50
bg-[#1e40af]/87

Now used across:

  • Backgrounds
  • Borders
  • Text
  • Rings
  • Gradients

7) Margin type improvements

Margins now use TWSizeOrFraction instead of TWSize.

This enables:

mx .~~ auto

instead of string-based workarounds.


8) Tests & documentation

Added

  • Comprehensive transition test suite
  • Transform tests
  • Gradient tests
  • HTML generator for visual verification
  • README rewrite
  • Migration guide
  • Usage examples

All test suites pass.


9) Tooling & cleanup

  • Henforcer compliance across 65 modules
  • Removed Chrome auto-open from tests
  • Cabal exports updated
  • General refactors and import cleanup

Result

This PR upgrades the library from a global transition model to a fully composable, type-safe styling system with:

  • Per-property transitions
  • Full transform support
  • Type-safe gradients
  • Unified color opacity
  • Better type inference
  • Stronger test coverage

augyg and others added 30 commits February 19, 2026 14:41
Add core WithTransition module that enables property-specific transitions
rather than global transitions. Key features:

- WithTransition wrapper type pairs values with optional TransitionConfig
- AutoWrap typeclass for backwards compatibility with existing code
- Builder pattern: withTransition, withTiming, withDelay
- All-at-once function: withTransitionAll
- Rendering helpers: renderWithTransitionTW, compileWithTransitionTW
- Simplified TransitionConfig (no WhenTW) to avoid illegal nesting
- Preserved TransitionConfigGlobal for backwards compatibility

Transitions now apply to individual property values across screen sizes
and pseudo-classes (hover, focus, etc.) instead of globally.
Update background color, opacity, and shadow to support per-value
transitions:

- _bgColor: WhenTW Color -> WhenTW (WithTransition Color)
- _bgOpacity: WhenTW Int -> WhenTW (WithTransition Int)
- _shadow: WhenTW BoxShadow -> WhenTW (WithTransition BoxShadow)
- _transition: TransitionConfig -> TransitionConfigGlobal

Remove local renderWithTransitionTW and compileWithTransitionTW
definitions in favor of importing from Classh.WithTransition module.

Update ShowTW and CompileStyle instances to use the new helpers with
proper TransitionProperty inference (Transition_Colors for colors,
Transition_Opacity for opacity, etc.).
Update all border properties to support per-value transitions:

Border Color (BorderColorSides):
- All fields: WhenTW Color -> WhenTW (WithTransition Color)
- Add FlexibleInstances pragma for SetSides instance

Border Width (BorderWidthSides):
- All fields: WhenTW BorderWidth -> WhenTW (WithTransition BorderWidth)
- Add FlexibleInstances pragma for SetSides instance

Border Radius (BorderRadiusCorners):
- All fields: WhenTW BorderRadius' -> WhenTW (WithTransition BorderRadius')
- Add FlexibleInstances pragma for SetSides instance

Remove local renderWithTransitionTW definitions, use centralized version
from Classh.WithTransition module.
Update padding and margin properties to support per-value transitions:

BoxPadding:
- All fields: WhenTW TWSize -> WhenTW (WithTransition TWSize)
- Add FlexibleInstances pragma for SetSides instance
- Remove local renderWithTransitionTW/compileWithTransitionTW

BoxMargin:
- All fields: WhenTW TWSize -> WhenTW (WithTransition TWSize)
- Add FlexibleInstances pragma for SetSides instance
- Remove local renderWithTransitionTW definition

Both modules now use centralized rendering helpers from
Classh.WithTransition module.
Update all sizing-related properties to support per-value transitions:

BoxSizing:
- _width: WhenTW TWSizeOrFraction -> WhenTW (WithTransition TWSizeOrFraction)
- _height: WhenTW TWSizeOrFraction -> WhenTW (WithTransition TWSizeOrFraction)

BoxSizingConstraint:
- _widthC: WhenTW DimensionConstraint -> WhenTW (WithTransition DimensionConstraint)
- _heightC: WhenTW DimensionConstraint -> WhenTW (WithTransition DimensionConstraint)

SizingBand:
- Remove local renderWithTransitionTW definition
- Use centralized version from Classh.WithTransition

This enables smooth transitions for width, height, min/max constraints
across responsive breakpoints and pseudo-classes.
Update RingConfig to support per-value transitions:

- _ringWidth: WhenTW RingWidth -> WhenTW (WithTransition RingWidth)
- _ringColor: WhenTW Color -> WhenTW (WithTransition Color)
- _ringOpacity: WhenTW Int -> WhenTW (WithTransition Int)

Remove local renderWithTransitionTW definition, use centralized version
from Classh.WithTransition module.

Enables smooth transitions for focus rings with proper transition property
inference (Transition_All for width, Transition_Colors for color,
Transition_Opacity for opacity).
…ility

Update all setter operators to use AutoWrap typeclass:

- (.~~): Single value setter now auto-wraps plain values in WithTransition
- (.|~): List-based setter now auto-wraps plain values in WithTransition
- (.~+): Append setter now auto-wraps plain values in WithTransition
- (.~^): New explicit transition setter for [(TWCondition, WithTransition a)]

This enables backwards compatibility, allowing existing code like:
  bgColor .~~ purple
to work seamlessly with new WithTransition types, while also supporting:
  bgColor .~~ (purple `withTransition` Duration_300)

The AutoWrap typeclass automatically wraps bare values when needed.
Update all shorthand setter type signatures to match new WithTransition
types:

Border shortcuts:
- br_*, bw_*, bc_*: Now use WhenTW (WithTransition X)

Sizing shortcuts:
- w, h, width', height': WhenTW (WithTransition TWSizeOrFraction)
- maxW, minW, maxH, minH: WhenTW (WithTransition DimensionConstraint)

Spacing shortcuts:
- mt, mb, ml, mr, mx, my, m: WhenTW (WithTransition TWSize)
- pt, pb, pl, pr, px, py, p: WhenTW (WithTransition TWSize)

All shortcuts now support both plain values (auto-wrapped) and explicit
transitions via the AutoWrap typeclass.
Add Classh.WithTransition to exposed-modules in ClasshSS.cabal.

Also add documentation comment to WhenTW module explaining that
WithTransition rendering helpers are defined in a separate module
to avoid circular dependencies.
Add TransitionTest.hs demonstrating various transition usage patterns:

- Test 1: Backwards compatibility with plain values (no transitions)
- Test 2: Using (.~^) operator with noTransition and withTransition
- Test 3: Builder pattern with chaining (withTransition, withTiming, withDelay)
- Test 4: All-at-once style with withTransitionAll

Tests cover responsive breakpoints (sm), pseudo-classes (hover, focus),
and various transition configurations (duration, timing, delay).
The AutoWrap typeclass is no longer needed as we've replaced it with
a more robust approach using SetConstant, SetResponsive, and AddResponsive
typeclasses that properly handle type inference.
Replace AutoWrap with SetConstant, SetResponsive, and AddResponsive
typeclasses that use type families (UnwrapType) to enable proper type
inference from lens types.

This allows expressions like `h .~~ pct 100` to work without type
annotations - the compiler infers that pct should return TWSizeOrFraction
based on the height lens type.

Key changes:
- Add SetConstant typeclass for (.~~) operator
- Add SetResponsive typeclass for (.|~) operator
- Add AddResponsive typeclass for (.|+) operator
- Use UnwrapType family to extract unwrapped types
- For WithTransition fields, automatically wrap values with noTransition
- For plain fields, use values directly
- Remove global _transition field from BoxConfig
- Update transition rendering to use arbitrary value syntax: [transition:property_duration_timing_delay]
- Add transitionPropertyToCSSName and transitionTimingToCSSName helpers
- Fix timing function conversion (in-out vs inout)
- Add functional dependency to SetSides class for better type inference
- Change from INCOHERENT to OVERLAPPING/OVERLAPPABLE pragmas in Setters

All transitions are now per-attribute using the WithTransition wrapper type,
generating Tailwind arbitrary values like:
hover:[transition:background-color,border-color,color,fill,stroke_300ms_in-out_0ms]
- Add TransitionTest.hs with 4 transition pattern tests
- Add ComprehensiveTest.hs with 11 property tests covering all ClasshSS features
- Add GenerateHTMLTest.hs with visual HTML generation for browser testing
- Add 10+ unique transition configurations across 6 responsive breakpoints
- Include tests for: durations (75ms-1000ms), timing functions (linear/ease-in/ease-out/ease-in-out), delays (0ms-1000ms)
- Generate test-output.html with rainbow spectrum (Red→Orange→Yellow→Green→Blue→Purple) for easy visual verification
- Automatically opens generated HTML in Chrome for manual testing

All tests pass (15/15 total) and verify the arbitrary transition syntax works correctly.
The google-chrome-stable executable doesn't exist on all systems.
Now the test just generates the HTML file and prompts the user to open it manually.
Add comprehensive transform support to BoxConfig with all Tailwind v3 transform properties:

Transform properties added:
- Rotate: standard values (0°, 1°, 2°, 3°, 6°, 12°, 45°, 90°, 180°) + custom
- Scale: standard values (0, 50, 75, 90, 95, 100, 105, 110, 125, 150) + custom
- Translate: X/Y axes with TWSize values, fractions, px, full, and custom CSS sizes
- Skew: X/Y axes with standard values (0°, 1°, 2°, 3°, 6°, 12°) + custom
- Transform Origin: all 9 standard positions (center, corners, edges) + custom

All transforms support smooth transitions via withTransition, allowing animated
transform changes on hover, focus, and other states.

Changes:
- Add TransformConfig with all transform properties as fields
- Add transform field to BoxConfig
- Create comprehensive test suite (TransformTest.hs) with 15 tests
- Fix name collision with Control.Lens.transform by hiding it
- Fix variable shadowing in Classh.hs (transform -> mutation)
- Add -Wall -Werror to test suites for stricter compile-time checks
- Clean up unused imports in test files
- Update README with layout philosophy (no flexbox by design)

All tests pass (15/15 transform tests, plus existing test suites).
- Rewrite README for clarity
- Add EXAMPLE.md with usage patterns
- Add MIGRATION_FROM_TAILWIND.md guide
- Add GradientColor type (SolidColor | GradientColor)
- Add GradientDirection, ColorStop, StopPosition types
- Add helper functions: solidColor, linearGradient, linearGradientVia, etc.
- Support stop positions (from-10%, via-30%, to-90%)
- Fix responsive/state prefix application for multi-class gradients
- Add gradient-test suite (13 tests)

BREAKING CHANGE: bgColor now requires GradientColor type.
Use solidColor wrapper for simple colors:
  bgColor .~~ solidColor (Blue C500)
- Wrap all bgColor values with solidColor helper
- All test suites passing (transition, generate-html, comprehensive, gradient)
Add support for colors with embedded opacity using Tailwind's
/opacity syntax (e.g., bg-blue-500/50, bg-[#1e40af]/87).

- Add ColorWithOpacity data type with color and opacity fields
- Add withOpacity helper function
- Add ShowTW instance rendering color/opacity format
- Add explicit module exports
Extend gradient API with opacity support for both solid colors
and gradient color stops.

Solid colors with opacity:
- Add SolidColorWithOpacity constructor to GradientColor
- Add solidColorOpacity helper for bg-[#hex]/opacity syntax

Gradient color stops with opacity:
- Add _stop_opacity field to ColorStop
- Add stopWithOpacity and stopAtWithOpacity helpers
- Update renderStop to include /opacity in output

This enables patterns like:
  bgColor .~~ solidColorOpacity (hex "4E366C") 87
  linearGradientPos To_BR (stopAtWithOpacity (hex "281C40") 90 0) ...
- Change _cwo_opacity from Int to Maybe Int (Nothing = fully opaque)
- Add 'color' smart constructor for Color -> ColorWithOpacity
- Update ShowTW instance to render opacity with Tailwind /n syntax
- This enables consistent opacity support across all color-using APIs
- Remove redundant SolidColorWithOpacity constructor from GradientColor
- SolidColor now takes ColorWithOpacity directly
- Simplify ColorStop to use ColorWithOpacity instead of separate opacity field
- Add solidColorOpacity and stopWithOpacity helper functions
- Update renderBgColorRaw for simplified GradientColor type
- Update BorderColorConfig fields to use ColorWithOpacity
- Update SetSides instance for border color
- Update RingConfig ringColor to use ColorWithOpacity
- Update TextConfigTW text_color to use ColorWithOpacity
- Update TextDecorationConfigTW textDec_color to use ColorWithOpacity
- Update Shorthand bc* lenses for new type signatures
- Add testSolidColorOpacity: solid color with /87 opacity
- Add testStopWithOpacity: gradient stop with opacity
- Add testStopAtWithOpacity: positioned stop with opacity
- Add testGradientMixedOpacity: mixed opacity and non-opacity stops
- Wrap bare Color values with 'color' helper in ComprehensiveTest
- Wrap bare Color values with 'color' helper in GenerateHTMLTest
Enables type-safe mx/ml/mr .~~ TWSize_Auto instead of custom .~ "mx-auto".
Margin shorthands (mt, mb, ml, mr, mx, my, m) now accept TWSizeOrFraction,
which includes Auto, Full, Fraction etc. in addition to numeric TWSize values.
Call sites use twSize' N instead of TWSize N for margin values.
Add henforcer.toml with max strictness config. Fix open unaliased
imports across all modules by adding explicit import lists or aliases.
Replace duplicate 'as X' re-export aliases with unique module aliases
and update export lists accordingly. Add missing module header fields
(copyright, license, maintainer) where absent.
@augyg augyg changed the title Gs/transition by property model Gs/transition by property model (ClasshSS 2.0) Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant