///|
///|
pub(all) enum ColorScheme {
Light
Dark
} derive(Eq, Debug, ToJson)
///|
pub(all) struct ColorPalette {
foreground : Color
background : Color
surface : Color
surface_variant : Color
primary : Color
primary_foreground : Color
outline : Color
muted : Color
} derive(Eq, Debug, ToJson)
///|
pub(all) struct SpacingScale {
xs : Double
sm : Double
md : Double
lg : Double
xl : Double
} derive(Eq, Debug, ToJson)
///|
pub(all) struct RadiusScale {
sm : Double
md : Double
lg : Double
pill : Double
} derive(Eq, Debug, ToJson)
///|
pub(all) struct TypographyScale {
body : FontSpec
title : FontSpec
caption : FontSpec
control : FontSpec
} derive(Eq, Debug, ToJson)
///|
pub(all) struct ShadowScale {
sm : ShadowStyle
md : ShadowStyle
lg : ShadowStyle
} derive(Eq, Debug, ToJson)
///|
pub(all) struct MotionScale {
fast_ms : Double
normal_ms : Double
slow_ms : Double
easing : Easing
} derive(Eq, Debug, ToJson)
///|
pub(all) struct SurfaceScale {
base : Brush
raised : Brush
overlay : Brush
} derive(Eq, Debug, ToJson)
///|
pub(all) enum SurfaceLevel {
Base
Raised
Overlay
} derive(Eq, Debug, ToJson)
///|
pub(all) enum LayoutDirection {
LeftToRight
RightToLeft
} derive(Eq, Debug, ToJson)
///|
pub(all) enum AccessibilityContrast {
Standard
High
} derive(Eq, Debug, ToJson)
///|
pub(all) enum ContentSizeCategory {
Small
Medium
Large
ExtraLarge
Accessibility
} derive(Eq, Debug, ToJson)
///|
pub(all) struct Theme {
scheme : ColorScheme
palette : ColorPalette
spacing_scale : SpacingScale
radius_scale : RadiusScale
typography : TypographyScale
shadow_scale : ShadowScale
motion : MotionScale
surfaces : SurfaceScale
font : FontSpec
foreground : Color
background : Color
accent : Color
spacing : Double
corner_radius : Double
} derive(Eq, Debug, ToJson)
///|
pub(all) struct Environment {
theme : Theme
color_scheme : ColorScheme
scale_factor : Double
text_scale : Double
locale : String
layout_direction : LayoutDirection
accessibility_contrast : AccessibilityContrast
reduced_motion : Bool
content_size_category : ContentSizeCategory
enabled : Bool
} derive(Eq, Debug, ToJson)
///|
pub fn ColorPalette::light() -> ColorPalette {
{
foreground: Color::black(),
background: Color::white(),
surface: Color::rgba(r=0.96, g=0.97, b=0.98),
surface_variant: Color::rgba(r=0.90, g=0.92, b=0.95),
primary: Color::blue(),
primary_foreground: Color::white(),
outline: Color::gray(),
muted: Color::rgba(r=0.42, g=0.45, b=0.50),
}
}
///|
pub fn ColorPalette::dark() -> ColorPalette {
{
foreground: Color::rgba(r=0.94, g=0.95, b=0.96),
background: Color::rgba(r=0.08, g=0.09, b=0.11),
surface: Color::rgba(r=0.13, g=0.15, b=0.18),
surface_variant: Color::rgba(r=0.20, g=0.23, b=0.28),
primary: Color::rgba(r=0.42, g=0.58, b=1.0),
primary_foreground: Color::white(),
outline: Color::rgba(r=0.34, g=0.38, b=0.45),
muted: Color::rgba(r=0.64, g=0.67, b=0.72),
}
}
///|
pub fn SpacingScale::default() -> SpacingScale {
{ xs: 4.0, sm: 8.0, md: 12.0, lg: 16.0, xl: 24.0 }
}
///|
pub fn RadiusScale::default() -> RadiusScale {
{ sm: 4.0, md: 8.0, lg: 12.0, pill: 999.0 }
}
///|
pub fn TypographyScale::default() -> TypographyScale {
{
body: FontSpec::new(size=16.0, weight=400),
title: FontSpec::new(size=22.0, weight=700),
caption: FontSpec::new(size=13.0, weight=400),
control: FontSpec::new(size=16.0, weight=600),
}
}
///|
pub fn ShadowScale::default(
scheme? : ColorScheme = ColorScheme::Light,
) -> ShadowScale {
let alpha = match scheme {
Light => 0.18
Dark => 0.36
}
{
sm: ShadowStyle::new(
color=Color::rgba(r=0.0, g=0.0, b=0.0, a=alpha * 0.65),
offset=Point::new(x=0.0, y=1.0),
blur_radius=6.0,
),
md: ShadowStyle::new(
color=Color::rgba(r=0.0, g=0.0, b=0.0, a=alpha),
offset=Point::new(x=0.0, y=4.0),
blur_radius=16.0,
),
lg: ShadowStyle::new(
color=Color::rgba(r=0.0, g=0.0, b=0.0, a=alpha * 1.2),
offset=Point::new(x=0.0, y=10.0),
blur_radius=28.0,
spread=2.0,
),
}
}
///|
pub fn MotionScale::default() -> MotionScale {
{
fast_ms: 120.0,
normal_ms: 180.0,
slow_ms: 280.0,
easing: Easing::EaseInOut,
}
}
///|
pub fn SurfaceScale::default(
palette : ColorPalette,
scheme : ColorScheme,
) -> SurfaceScale {
let raised = match scheme {
Light =>
Brush::linear_gradient(
start=Point::new(x=0.0, y=0.0),
end=Point::new(x=0.0, y=120.0),
start_color=Color::white(),
end_color=palette.surface,
)
Dark =>
Brush::linear_gradient(
start=Point::new(x=0.0, y=0.0),
end=Point::new(x=0.0, y=120.0),
start_color=palette.surface_variant,
end_color=palette.surface,
)
}
{
base: Brush::solid(palette.surface),
raised,
overlay: Brush::solid(palette.surface_variant),
}
}
///|
pub fn Theme::light() -> Theme {
let palette = ColorPalette::light()
let spacing_scale = SpacingScale::default()
let radius_scale = RadiusScale::default()
let typography = TypographyScale::default()
let shadow_scale = ShadowScale::default(scheme=ColorScheme::Light)
let motion = MotionScale::default()
let surfaces = SurfaceScale::default(palette, ColorScheme::Light)
{
scheme: ColorScheme::Light,
palette,
spacing_scale,
radius_scale,
typography,
shadow_scale,
motion,
surfaces,
font: typography.body,
foreground: palette.foreground,
background: palette.background,
accent: palette.primary,
spacing: spacing_scale.sm,
corner_radius: radius_scale.md,
}
}
///|
pub fn Theme::dark() -> Theme {
let palette = ColorPalette::dark()
let spacing_scale = SpacingScale::default()
let radius_scale = RadiusScale::default()
let typography = TypographyScale::default()
let shadow_scale = ShadowScale::default(scheme=ColorScheme::Dark)
let motion = MotionScale::default()
let surfaces = SurfaceScale::default(palette, ColorScheme::Dark)
{
scheme: ColorScheme::Dark,
palette,
spacing_scale,
radius_scale,
typography,
shadow_scale,
motion,
surfaces,
font: typography.body,
foreground: palette.foreground,
background: palette.background,
accent: palette.primary,
spacing: spacing_scale.sm,
corner_radius: radius_scale.md,
}
}
///|
pub fn Theme::default() -> Theme {
Theme::light()
}
///|
pub fn Theme::with_font(self : Theme, font : FontSpec) -> Theme {
{ ..self, font, typography: { ..self.typography, body: font } }
}
///|
pub fn Theme::with_foreground(self : Theme, color : Color) -> Theme {
{ ..self, foreground: color, palette: { ..self.palette, foreground: color } }
}
///|
pub fn Theme::with_background(self : Theme, color : Color) -> Theme {
{ ..self, background: color, palette: { ..self.palette, background: color } }
}
///|
pub fn Theme::with_accent(self : Theme, color : Color) -> Theme {
{ ..self, accent: color, palette: { ..self.palette, primary: color } }
}
///|
pub fn Theme::with_spacing(self : Theme, spacing : Double) -> Theme {
{ ..self, spacing, spacing_scale: { ..self.spacing_scale, sm: spacing } }
}
///|
pub fn Theme::with_corner_radius(self : Theme, radius : Double) -> Theme {
{
..self,
corner_radius: radius,
radius_scale: { ..self.radius_scale, md: radius },
}
}
///|
pub fn Theme::with_palette(self : Theme, palette : ColorPalette) -> Theme {
let surfaces = SurfaceScale::default(palette, self.scheme)
{
..self,
palette,
surfaces,
foreground: palette.foreground,
background: palette.background,
accent: palette.primary,
}
}
///|
pub fn Theme::with_spacing_scale(
self : Theme,
spacing_scale : SpacingScale,
) -> Theme {
{ ..self, spacing_scale, spacing: spacing_scale.sm }
}
///|
pub fn Theme::with_radius_scale(
self : Theme,
radius_scale : RadiusScale,
) -> Theme {
{ ..self, radius_scale, corner_radius: radius_scale.md }
}
///|
pub fn Theme::with_typography(
self : Theme,
typography : TypographyScale,
) -> Theme {
{ ..self, typography, font: typography.body }
}
///|
pub fn Theme::with_shadow_scale(
self : Theme,
shadow_scale : ShadowScale,
) -> Theme {
{ ..self, shadow_scale, }
}
///|
pub fn Theme::with_motion(self : Theme, motion : MotionScale) -> Theme {
{ ..self, motion, }
}
///|
pub fn Theme::with_surfaces(self : Theme, surfaces : SurfaceScale) -> Theme {
{ ..self, surfaces, }
}
///|
pub fn Theme::surface_brush(self : Theme, level : SurfaceLevel) -> Brush {
match level {
Base => self.surfaces.base
Raised => self.surfaces.raised
Overlay => self.surfaces.overlay
}
}
///|
pub fn Environment::default() -> Environment {
let theme = Theme::default()
{
theme,
color_scheme: theme.scheme,
scale_factor: 1.0,
text_scale: 1.0,
locale: "en-US",
layout_direction: LayoutDirection::LeftToRight,
accessibility_contrast: AccessibilityContrast::Standard,
reduced_motion: false,
content_size_category: ContentSizeCategory::Medium,
enabled: true,
}
}
///|
pub fn Environment::with_theme(
self : Environment,
theme : Theme,
) -> Environment {
{ ..self, theme, color_scheme: theme.scheme }
}
///|
pub fn Environment::with_color_scheme(
self : Environment,
color_scheme : ColorScheme,
) -> Environment {
let theme = match color_scheme {
Light => Theme::light()
Dark => Theme::dark()
}
{ ..self, theme, color_scheme }
}
///|
pub fn Environment::with_scale_factor(
self : Environment,
scale_factor : Double,
) -> Environment {
{ ..self, scale_factor: max_double(0.1, scale_factor) }
}
///|
pub fn Environment::with_text_scale(
self : Environment,
text_scale : Double,
) -> Environment {
{ ..self, text_scale: max_double(0.1, text_scale) }
}
///|
pub fn Environment::with_locale(
self : Environment,
locale : String,
) -> Environment {
{ ..self, locale, }
}
///|
pub fn Environment::with_layout_direction(
self : Environment,
layout_direction : LayoutDirection,
) -> Environment {
{ ..self, layout_direction, }
}
///|
pub fn Environment::with_accessibility_contrast(
self : Environment,
accessibility_contrast : AccessibilityContrast,
) -> Environment {
{ ..self, accessibility_contrast, }
}
///|
pub fn Environment::with_reduced_motion(
self : Environment,
reduced_motion : Bool,
) -> Environment {
{ ..self, reduced_motion, }
}
///|
pub fn Environment::with_content_size_category(
self : Environment,
content_size_category : ContentSizeCategory,
) -> Environment {
{
..self,
content_size_category,
text_scale: content_size_category.text_scale(),
}
}
///|
pub fn Environment::with_enabled(
self : Environment,
enabled : Bool,
) -> Environment {
{ ..self, enabled, }
}
///|
pub fn ContentSizeCategory::text_scale(self : ContentSizeCategory) -> Double {
match self {
Small => 0.9
Medium => 1.0
Large => 1.15
ExtraLarge => 1.3
Accessibility => 1.6
}
}
///|
pub fn ViewStyle::from_theme(theme : Theme) -> ViewStyle {
{
font: Some(theme.font),
foreground: Some(theme.foreground),
background: Some(theme.surface_brush(SurfaceLevel::Base)),
corner_radius: Some(theme.corner_radius),
}
}