A type-safe CSS style system for the Spark framework.
spark_css provides a comprehensive set of typed CSS value classes and a stylesheet API for building CSS in Dart. Instead of working with raw strings, you use typed constructors like CssColor.hex(), CssLength.rem(), and CssDisplay.flex that guarantee valid CSS output at compile time.
- Type-Safe CSS Values: Sealed classes for colors, lengths, spacing, display, position, flexbox, typography, borders, transitions, filters, transforms, shadows, cursors, backgrounds, and more.
- Stylesheet API:
Style.typedfor individual rule sets andcss()helper for multi-selector stylesheets. - CSS Shorthand Support:
CssSpacingandCssBorderRadiushandle 1–4 value shorthand for margin, padding, and border-radius. - CSS Functions: Support for
calc(),min(),max(),clamp(), andfit-content(). - CSS Variables: Every value type supports
variable()for CSS custom properties. - Global Keywords: Every value type supports
global()forinherit,initial,unset,revert, andrevert-layer. - Filters & Transforms: Full
CssFilterandCssTransformAPIs with composition support. - Gradients & Backgrounds: Linear and radial gradients,
CssBackgroundshorthand, plus typed background-size, position, repeat, clip, origin, and attachment. - Grid:
CssGridTemplateColumnsandCssTrackSizefor type-safe grid track definitions withrepeat(),auto-fill,auto-fit,minmax(), andfit-content(). - Shadows:
CssBoxShadowandCssTextShadowwith multi-shadow support. - Automatic Minification: CSS output is minified in production builds via the
dart.vm.productflag. - Component Style Registry: Server-side style deduplication via
componentStyles. - Escape Hatches:
raw()factories and.add()method for properties not yet covered by typed constructors. - Zero Dependencies: Pure Dart package with no runtime dependencies.
dart pub add spark_cssImport the package:
import 'package:spark_css/spark_css.dart';Use Style.typed to build a type-safe set of CSS properties:
final style = Style.typed(
display: CssDisplay.flex,
padding: CssSpacing.all(CssLength.px(16)),
backgroundColor: CssColor.hex('f5f5f5'),
borderRadius: CssBorderRadius.all(CssLength.px(8)),
);
print(style.toCss());
// background-color: #f5f5f5;
// border-radius: 8px;
// display: flex;
// padding: 16px;Use the css() helper to map selectors to styles:
final styles = css({
':host': Style.typed(
display: CssDisplay.flex,
flexDirection: CssFlexDirection.column,
gap: CssLength.rem(1),
),
':host(.large)': Style.typed(
padding: CssSpacing.all(CssLength.px(24)),
),
});
print(styles.toCss());CssSpacing mirrors CSS shorthand conventions:
// Single value (all sides)
padding: CssSpacing.all(CssLength.px(16)),
// Two values (vertical | horizontal)
margin: CssSpacing.symmetric(CssLength.px(10), CssLength.px(20)),
// Four values (top | right | bottom | left)
margin: CssSpacing.trbl(
CssLength.px(10),
CssLength.px(20),
CssLength.px(30),
CssLength.px(40),
),
// Named parameters for specific sides
margin: CssSpacing.sides(top: CssLength.px(10), left: CssLength.px(20)),CssBorderRadius follows the same shorthand pattern:
// Single value (all corners)
borderRadius: CssBorderRadius.all(CssLength.px(8)),
// Two values (topLeft+bottomRight | topRight+bottomLeft)
borderRadius: CssBorderRadius.symmetric(CssLength.px(8), CssLength.px(4)),
// Four values (topLeft | topRight | bottomRight | bottomLeft)
borderRadius: CssBorderRadius.trbl(
CssLength.px(8),
CssLength.px(4),
CssLength.px(8),
CssLength.px(4),
),Every value type supports CSS custom properties:
Style.typed(
color: CssColor.variable('text-primary'),
fontSize: CssLength.variable('font-size-base'),
)Every value type supports CSS global keywords via CssGlobal:
Style.typed(
display: CssDisplay.global(CssGlobal.inherit),
color: CssColor.global(CssGlobal.unset),
)Available globals: inherit, initial, unset, revert, revertLayer.
CssFilter supports all CSS filter functions:
Style.typed(
filter: CssFilter.blur(CssLength.px(4)),
)
// Compose multiple filters
Style.typed(
filter: CssFilter.compose([
CssFilter.grayscalePercent(50),
CssFilter.blur(CssLength.px(2)),
]),
)
// Backdrop filter
Style.typed(
backdropFilter: CssFilter.blur(CssLength.px(10)),
)Available filters: blur, brightness, contrast, dropShadow, grayscale, hueRotate, invert, opacity, saturate, sepia. Each has both unitless and percent variants (e.g. brightness / brightnessPercent).
CssTransform supports all 2D transform functions:
Style.typed(
transform: CssTransform.rotate(CssAngle.deg(45)),
)
// Compose multiple transforms
Style.typed(
transform: CssTransform.list([
CssTransform.translate(CssLength.px(10), CssLength.px(20)),
CssTransform.scale(1.5),
CssTransform.rotate(CssAngle.deg(45)),
]),
)Available transforms: translate, translateX, translateY, scale, scaleX, scaleY, rotate, skew, skewX, skewY, matrix.
CssBoxShadow and CssTextShadow support single and multiple shadows:
// Box shadow
Style.typed(
boxShadow: CssBoxShadow(
x: CssLength.px(0),
y: CssLength.px(4),
blur: CssLength.px(8),
color: CssColor.rgba(0, 0, 0, 0.1),
),
)
// Multiple box shadows
Style.typed(
boxShadow: CssBoxShadow.multiple([
CssBoxShadow(
x: CssLength.px(0),
y: CssLength.px(2),
blur: CssLength.px(4),
color: CssColor.rgba(0, 0, 0, 0.1),
),
CssBoxShadow(
x: CssLength.px(0),
y: CssLength.px(8),
blur: CssLength.px(16),
color: CssColor.rgba(0, 0, 0, 0.1),
inset: true,
),
]),
)
// Text shadow
Style.typed(
textShadow: CssTextShadow(
x: CssLength.px(1),
y: CssLength.px(1),
blur: CssLength.px(2),
color: CssColor.rgba(0, 0, 0, 0.3),
),
)CssBackgroundImage supports linear and radial gradients:
// Linear gradient
Style.typed(
backgroundImage: CssBackgroundImage.linearGradient(
direction: CssGradientDirection.toRight,
stops: [
CssGradientStop(CssColor.hex('ff0000')),
CssGradientStop(CssColor.hex('0000ff')),
],
),
)
// Radial gradient with stop offsets
Style.typed(
backgroundImage: CssBackgroundImage.radialGradient(
shape: CssRadialShape.circle,
stops: [
CssGradientStop(CssColor.hex('ff0000'), CssLength.percent(0)),
CssGradientStop(CssColor.hex('0000ff'), CssLength.percent(100)),
],
),
)Typed background properties: backgroundSize, backgroundPosition, backgroundRepeat, backgroundClip, backgroundOrigin, backgroundAttachment.
CssBackground provides the background shorthand:
// Simple color background
Style.typed(background: CssBackground.color(CssColor.hex('f5f5f5')))
// Full shorthand with position/size slash syntax
Style.typed(
background: CssBackground.shorthand(
image: CssBackgroundImage.url('bg.png'),
position: CssBackgroundPosition.center,
size: CssBackgroundSize.cover,
repeat: CssBackgroundRepeat.noRepeat,
color: CssColor.white,
),
)
// Multiple background layers
Style.typed(
background: CssBackground.layers([
CssBackground.shorthand(
image: CssBackgroundImage.url('overlay.png'),
repeat: CssBackgroundRepeat.noRepeat,
),
CssBackground.color(CssColor.white),
]),
)CssTransition uses typed parameters for property names, durations, and timing functions:
Style.typed(
transition: CssTransition.simple(
CssTransitionProperty.opacity,
CssDuration.ms(200),
CssTimingFunction.easeInOut,
),
)
// Full control with delay
CssTransition(
property: CssTransitionProperty.transform,
duration: CssDuration.s(0.3),
timingFunction: CssTimingFunction.easeInOut,
delay: CssDuration.ms(100),
)
// Multiple transitions
Style.typed(
transition: CssTransition.multiple([
CssTransition.simple(CssTransitionProperty.opacity, CssDuration.ms(200)),
CssTransition.simple(CssTransitionProperty.transform, CssDuration.ms(300)),
]),
)
// Raw escape hatches for unsupported values
CssTransitionProperty.raw('max-width')
CssDuration.raw('200ms')
// Custom timing function
CssTimingFunction.cubicBezier(0.4, 0, 0.2, 1)CssFlexShorthand provides the flex shorthand with smart validation:
// flex: auto
Style.typed(flex: CssFlexShorthand.auto)
// flex: 1 (grow only)
Style.typed(flex: CssFlexShorthand(grow: 1))
// flex: 1 0 auto (grow, shrink, basis)
Style.typed(flex: CssFlexShorthand(grow: 1, shrink: 0, basis: CssLength.auto))CssFontFamily supports named fonts, generic families, and font stacks:
// Generic family
Style.typed(fontFamily: CssFontFamily.sansSerif)
// Named font
Style.typed(fontFamily: CssFontFamily.named('Inter'))
// Font stack with fallbacks
Style.typed(
fontFamily: CssFontFamily.stack([
CssFontFamily.named('Inter'),
CssFontFamily.named('Helvetica'),
CssFontFamily.sansSerif,
]),
)CssOutline provides the outline shorthand:
Style.typed(
outline: CssOutline(
width: CssLength.px(2),
style: CssBorderStyle.solid,
color: CssColor.hex('0066ff'),
),
outlineOffset: CssLength.px(2),
)CssCursor covers all standard CSS cursors plus custom URL cursors:
Style.typed(cursor: CssCursor.pointer)
// Custom cursor with fallback
Style.typed(
cursor: CssCursor.url('custom.cur', fallback: CssCursor.pointer),
)CssLength supports modern viewport units alongside classic ones:
// Dynamic viewport units
height: CssLength.dvh(100),
width: CssLength.dvw(100),
// Small viewport units
height: CssLength.svh(100),
// Large viewport units
height: CssLength.lvh(100),CssGridTemplateColumns and CssTrackSize provide type-safe grid track definitions:
// Explicit tracks
Style.typed(
display: CssDisplay.grid,
gridTemplateColumns: CssGridTemplateColumns.tracks([
CssTrackSize.fr(1),
CssTrackSize.fr(2),
CssTrackSize.length(CssLength.px(200)),
]),
)
// repeat()
Style.typed(
gridTemplateColumns: CssGridTemplateColumns.repeat(3, [CssTrackSize.fr(1)]),
)
// auto-fill with minmax
Style.typed(
gridTemplateColumns: CssGridTemplateColumns.autoFill([
CssTrackSize.minmax(
CssTrackSize.length(CssLength.px(200)),
CssTrackSize.fr(1),
),
]),
)The accent-color CSS property is supported via CssColor:
Style.typed(accentColor: CssColor.hex('0066ff'))For properties not covered by the typed constructors, use .add():
final style = Style.typed(
display: CssDisplay.grid,
);
style.add('grid-auto-rows', 'minmax(100px, auto)');For server-side rendering, componentStyles provides style deduplication:
componentStyles.register('my-button', buttonStyles.toCss());
// Later, retrieve registered CSS
final css = componentStyles.get('my-button');This package is part of the Spark framework. Contributions are welcome at https://github.com/KLEAK-Development/spark.