Styling
Spark provides a typed CSS-in-Dart solution for styling your pages and components. You can use the string-based Style constructor for quick styling, or the type-safe Style.typed constructor with full IDE autocomplete and compile-time validation.
Page Styles
Override the inlineStyles getter in your SparkPage to inject CSS into the <head>.
@override
Stylesheet? get inlineStyles => css({
'body': Style(
backgroundColor: 'white',
color: '#333',
fontFamily: 'sans-serif',
),
'.container': Style(
maxWidth: '800px',
margin: '0 auto',
),
});Type-Safe Styling with Style.typed
The Style.typed constructor provides compile-time type safety and IDE autocomplete for CSS properties. Instead of passing strings, you use typed CSS value classes.
@override
Stylesheet? get inlineStyles => css({
'body': .typed(
backgroundColor: .white,
color: .hex('333'),
fontFamily: .sansSerif,
),
'.container': .typed(
maxWidth: .px(800),
margin: .symmetric(.zero, .auto),
),
});Benefits of Style.typed:
- Compile-time validation of CSS values
- IDE autocomplete for all CSS properties and values
- No typos in property names or values
- Self-documenting code
CSS Colors
The CssColor class supports all CSS color formats:
// Named colors
CssColor.black
CssColor.white
CssColor.transparent
CssColor.currentColor
// Hex colors
CssColor.hex('ff0000') // #ff0000
CssColor.hex('#00ff00') // #00ff00
// RGB / RGBA
CssColor.rgb(255, 0, 0) // rgb(255, 0, 0)
CssColor.rgba(255, 0, 0, 0.5) // rgba(255, 0, 0, 0.5)
// HSL / HSLA
CssColor.hsl(0, 100, 50) // hsl(0, 100%, 50%)
CssColor.hsla(0, 100, 50, 0.5) // hsla(0, 100%, 50%, 0.5)
// CSS variables
CssColor.variable('primary') // var(--primary)
// Raw escape hatch
CssColor.raw('color-mix(in srgb, red 50%, blue)')CSS Lengths
The CssLength class supports all CSS length units:
// Absolute units
CssLength.px(16) // 16px
CssLength.zero // 0
// Relative units
CssLength.em(1.5) // 1.5em
CssLength.rem(1) // 1rem
CssLength.percent(100) // 100%
// Viewport units
CssLength.vw(50) // 50vw
CssLength.vh(100) // 100vh
CssLength.dvh(100) // 100dvh (dynamic)
// Keywords
CssLength.auto // auto
CssLength.maxContent // max-content
CssLength.minContent // min-content
CssLength.fitContent // fit-content
// CSS functions
CssLength.calc('100% - 60px') // calc(100% - 60px)
CssLength.min([CssLength.px(100), CssLength.percent(50)]) // min(100px, 50%)
CssLength.max([CssLength.px(100), CssLength.percent(50)]) // max(100px, 50%)
CssLength.clamp(CssLength.px(10), CssLength.percent(50), CssLength.px(100))
// CSS variables
CssLength.variable('spacing') // var(--spacing)CSS Spacing
The CssSpacing class handles margin and padding with support for CSS shorthand syntax:
// Single value (all sides)
CssSpacing.all(CssLength.px(16)) // 16px
CssSpacing.zero // 0
// Two values (vertical | horizontal)
CssSpacing.symmetric(
CssLength.px(10), // vertical
CssLength.px(20), // horizontal
) // 10px 20px
// Three values (top | horizontal | bottom)
CssSpacing.only(
top: CssLength.px(10),
horizontal: CssLength.px(20),
bottom: CssLength.px(30),
) // 10px 20px 30px
// Four values (top | right | bottom | left)
CssSpacing.trbl(
CssLength.px(10), // top
CssLength.px(20), // right
CssLength.px(30), // bottom
CssLength.px(40), // left
) // 10px 20px 30px 40pxLayout Properties
Type-safe values for display, position, and flexbox:
// Display
CssDisplay.flex
CssDisplay.grid
CssDisplay.block
CssDisplay.inline
CssDisplay.inlineBlock
CssDisplay.inlineFlex
CssDisplay.none
// Position
CssPosition.static_
CssPosition.relative
CssPosition.absolute
CssPosition.fixed
CssPosition.sticky
// Flexbox
CssFlexDirection.row
CssFlexDirection.column
CssFlexDirection.rowReverse
CssFlexDirection.columnReverse
CssJustifyContent.flexStart
CssJustifyContent.flexEnd
CssJustifyContent.center
CssJustifyContent.spaceBetween
CssJustifyContent.spaceAround
CssJustifyContent.spaceEvenly
CssAlignItems.flexStart
CssAlignItems.flexEnd
CssAlignItems.center
CssAlignItems.stretch
CssAlignItems.baselineTypography
Type-safe values for font and text properties:
// Font weight
CssFontWeight.normal
CssFontWeight.bold
CssFontWeight.w100 // through w900
CssFontWeight.w400
CssFontWeight.w700
// Font family
CssFontFamily.sansSerif
CssFontFamily.serif
CssFontFamily.monospace
CssFontFamily.systemUi
CssFontFamily.named('Helvetica Neue') // "Helvetica Neue"
CssFontFamily.stack([ // font stack with fallbacks
CssFontFamily.named('Inter'),
CssFontFamily.sansSerif,
])
// Text align
CssTextAlign.left
CssTextAlign.center
CssTextAlign.right
CssTextAlign.justify
// Text decoration
CssTextDecoration.none
CssTextDecoration.underline
CssTextDecoration.lineThrough
// Text transform
CssTextTransform.uppercase
CssTextTransform.lowercase
CssTextTransform.capitalize
CssTextTransform.noneVisual Properties
Type-safe values for visual styling:
// Overflow
CssOverflow.visible
CssOverflow.hidden
CssOverflow.scroll
CssOverflow.auto
// Cursor
CssCursor.pointer
CssCursor.default_
CssCursor.text
CssCursor.move
CssCursor.notAllowed
CssCursor.grab
CssCursor.grabbing
// Numbers (opacity, line-height, flex-grow, etc.)
CssNumber(0.5) // 0.5
CssNumber(1.6) // 1.6
// Z-index
CssZIndex(100) // 100
CssZIndex.auto // autoBorders and Transitions
// Border shorthand
CssBorder(
width: CssLength.px(1),
style: CssBorderStyle.solid,
color: CssColor.variable('border-color'),
) // 1px solid var(--border-color)
CssBorder.none // none
// Border style
CssBorderStyle.solid
CssBorderStyle.dashed
CssBorderStyle.dotted
CssBorderStyle.none
// Transition
CssTransition.simple('color', '0.2s') // color 0.2s
CssTransition.simple('all', '0.3s', CssTimingFunction.ease) // all 0.3s ease
// Multiple transitions
CssTransition.multiple([
CssTransition.simple('opacity', '200ms'),
CssTransition.simple('transform', '300ms'),
]) // opacity 200ms, transform 300ms
// Timing functions
CssTimingFunction.ease
CssTimingFunction.easeIn
CssTimingFunction.easeOut
CssTimingFunction.easeInOut
CssTimingFunction.linear
CssTimingFunction.cubicBezier(0.4, 0, 0.2, 1)CSS Variables
Define CSS custom properties using the add() method and reference them with .variable():
// Define CSS variables in :root
':root': Style()
..add('--primary', '#007bff')
..add('--spacing', '16px')
..add('--border-color', '#eaeaea'),
// Use variables with .typed
'.button': .typed(
backgroundColor: .variable('primary'),
padding: .variable('spacing'),
borderColor: .variable('border-color'),
)Global Keywords
CSS global keywords are available on all type classes via .global():
// Inherit from parent
CssColor.global(CssGlobal.inherit)
// Reset to initial value
CssLength.global(CssGlobal.initial)
// Unset (inherit if inheritable, else initial)
CssDisplay.global(CssGlobal.unset)
// Revert to user-agent stylesheet
CssPosition.global(CssGlobal.revert)Raw Escape Hatch
For complex or unsupported CSS values, use the .raw() factory:
// Complex color functions
CssColor.raw('color-mix(in srgb, red 50%, blue)')
// Complex borders with variables
CssBorder.raw('1px solid var(--border-color)')
// Vendor prefixes or experimental features
.typed(
backdropFilter: 'blur(10px)', // String for complex values
transform: 'translateY(-2px) scale(1.05)',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
)Complete Example
Here's a complete example using Style.typed:
Stylesheet? get inlineStyles => css({
'.card': .typed(
display: .flex,
flexDirection: .column,
padding: .all(.px(24)),
margin: .symmetric(
.px(16),
.zero,
),
backgroundColor: .white,
borderRadius: .px(8),
border: CssBorder(
width: .px(1),
style: .solid,
color: .variable('border-color'),
),
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
transition: .simple(
'box-shadow',
'0.2s',
.ease,
),
),
'.card:hover': .typed(
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
),
'.card-title': .typed(
fontSize: .rem(1.5),
fontWeight: .w600,
color: .variable('text-main'),
marginBottom: .px(8),
),
'.card-content': .typed(
fontSize: .rem(1),
lineHeight: CssNumber(1.6),
color: .variable('text-muted'),
),
});Component Styles
For Web Components, include styles inside the shadow root using a <style> tag with the typed CSS syntax:
class MyButton extends WebComponent {
static const tag = 'my-button';
@override
String get tagName => tag;
static dynamic render({required String label}) {
return element(
tag,
[
template(shadowrootmode: 'open', [
style([
css({
':host': .typed(display: .inlineBlock),
'button': .typed(
padding: .symmetric(.px(8), .px(16)),
background: 'var(--primary)',
color: .white,
border: .none,
borderRadius: .px(4),
cursor: .pointer,
),
'button:hover': .typed(opacity: CssNumber(0.9)),
}).toCss(),
]),
button([label]),
]),
],
);
}
}