Files

160 lines
5.9 KiB
Odin
Raw Permalink Normal View History

2026-09-04 14:26:42 +02:00
package main
2026-09-04 16:08:21 +02:00
import engine "shared:zephr"
2026-09-04 14:26:42 +02:00
// Material 3 design tokens.
//
// Baseline dark scheme, verbatim from the M3 spec. Sizes are dp and type sizes
// are sp, which are the same thing here because layout points are dp.
//
// Two spec notes that shape everything below:
// - M3 conveys elevation with surface *colour*, not shadow. The engine draws
// no shadows, and it does not need to: the container roles below are the
// real mechanism.
// - Corner radii are uniform. The engine's border_radius is a single float,
// so shapes needing per-corner radii (segmented button ends) are noted.
// -- Colour roles ------------------------------------------------------------
hex :: proc(v: u32, a: f32 = 1) -> engine.Color {
return {
f32((v >> 16) & 0xFF) / 255,
f32((v >> 8) & 0xFF) / 255,
f32(v & 0xFF) / 255,
a,
}
}
M3 :: struct {
primary, on_primary, primary_container, on_primary_container: engine.Color,
secondary, on_secondary, secondary_container, on_secondary_container: engine.Color,
tertiary, on_tertiary, tertiary_container, on_tertiary_container: engine.Color,
error, on_error, error_container, on_error_container: engine.Color,
surface, on_surface, surface_variant, on_surface_variant: engine.Color,
surface_c_lowest, surface_c_low, surface_c, surface_c_high, surface_c_highest: engine.Color,
outline, outline_variant: engine.Color,
inverse_surface, inverse_on_surface, inverse_primary: engine.Color,
scrim: engine.Color,
}
m3: M3
m3_init_dark :: proc() {
m3.primary = hex(0xD0BCFF)
m3.on_primary = hex(0x381E72)
m3.primary_container = hex(0x4F378B)
m3.on_primary_container = hex(0xEADDFF)
m3.secondary = hex(0xCCC2DC)
m3.on_secondary = hex(0x332D41)
m3.secondary_container = hex(0x4A4458)
m3.on_secondary_container = hex(0xE8DEF8)
m3.tertiary = hex(0xEFB8C8)
m3.on_tertiary = hex(0x492532)
m3.tertiary_container = hex(0x633B48)
m3.on_tertiary_container = hex(0xFFD8E4)
m3.error = hex(0xF2B8B5)
m3.on_error = hex(0x601410)
m3.error_container = hex(0x8C1D18)
m3.on_error_container = hex(0xF9DEDC)
m3.surface = hex(0x141218)
m3.on_surface = hex(0xE6E0E9)
m3.surface_variant = hex(0x49454F)
m3.on_surface_variant = hex(0xCAC4D0)
// The elevation ladder. Higher role == "further from the surface".
m3.surface_c_lowest = hex(0x0F0D13)
m3.surface_c_low = hex(0x1D1B20)
m3.surface_c = hex(0x211F26)
m3.surface_c_high = hex(0x2B2930)
m3.surface_c_highest = hex(0x36343B)
m3.outline = hex(0x938F99)
m3.outline_variant = hex(0x49454F)
m3.inverse_surface = hex(0xE6E0E9)
m3.inverse_on_surface = hex(0x322F35)
m3.inverse_primary = hex(0x6750A4)
m3.scrim = hex(0x000000)
}
// State layer opacities, applied over a container colour.
M3_STATE_HOVER :: f32(0.08)
M3_STATE_FOCUS :: f32(0.10)
M3_STATE_PRESSED :: f32(0.10)
M3_DISABLED_FG :: f32(0.38)
M3_DISABLED_BG :: f32(0.12)
// Composites a state layer over a base colour, which is how M3 defines every
// hover/press appearance rather than by naming separate colours.
m3_state_layer :: proc(base: engine.Color, layer: engine.Color, opacity: f32) -> engine.Color {
return {
base.r + (layer.r - base.r) * opacity,
base.g + (layer.g - base.g) * opacity,
base.b + (layer.b - base.b) * opacity,
base.a,
}
}
m3_alpha :: proc(c: engine.Color, a: f32) -> engine.Color {
return {c.r, c.g, c.b, a}
}
// -- Type scale (sp) ---------------------------------------------------------
M3_DISPLAY_SMALL :: 36
M3_HEADLINE_MEDIUM :: 28
M3_HEADLINE_SMALL :: 24
M3_TITLE_LARGE :: 22
M3_TITLE_MEDIUM :: 16
M3_TITLE_SMALL :: 14
M3_BODY_LARGE :: 16
M3_BODY_MEDIUM :: 14
M3_BODY_SMALL :: 12
M3_LABEL_LARGE :: 14
M3_LABEL_MEDIUM :: 12
M3_LABEL_SMALL :: 11
// Roboto weights the M3 type scale uses. Title/label roles are medium (500).
M3_WEIGHT_REGULAR :: f32(400)
M3_WEIGHT_MEDIUM :: f32(500)
// -- Shape scale (dp) --------------------------------------------------------
M3_SHAPE_XS :: 4
M3_SHAPE_SM :: 8
M3_SHAPE_MD :: 12
M3_SHAPE_LG :: 16
M3_SHAPE_XL :: 28
M3_SHAPE_FULL :: 9999 // clamped by the widget's half-height
// -- Icon codepoints ---------------------------------------------------------
//
// Written as byte escapes rather than literal glyphs: these live in the Private
// Use Area, so they are invisible in an editor and are silently lost by tools
// that do not round-trip them. The codepoint in each comment is the source of
// truth, and must stay in step with the pyftsubset --unicodes list.
ICON_ARROW_BACK :: "\xEE\x97\x84" // U+E5C4 arrow_back
ICON_MENU :: "\xEE\x97\x92" // U+E5D2 menu
ICON_MORE_VERT :: "\xEE\x97\x94" // U+E5D4 more_vert
ICON_SEARCH :: "\xEE\xA2\xB6" // U+E8B6 search
ICON_CHECK :: "\xEE\x97\x8A" // U+E5CA check
ICON_CLOSE :: "\xEE\x97\x8D" // U+E5CD close
ICON_ADD :: "\xEE\x85\x85" // U+E145 add
ICON_HOME :: "\xEE\xA2\x8A" // U+E88A home
ICON_FAVORITE :: "\xEE\xA1\xBD" // U+E87D favorite
ICON_SETTINGS :: "\xEE\xA2\xB8" // U+E8B8 settings
ICON_EDIT :: "\xEE\x8F\x89" // U+E3C9 edit
ICON_DELETE :: "\xEE\xA1\xB2" // U+E872 delete
ICON_PERSON :: "\xEE\x9F\xBD" // U+E7FD person
ICON_STAR :: "\xEE\xA0\xB8" // U+E838 star
ICON_SHARE :: "\xEE\xA0\x8D" // U+E80D share
ICON_DOWNLOAD :: "\xEE\x8B\x84" // U+E2C4 download
ICON_BOOKMARK :: "\xEE\xA1\xA6" // U+E866 bookmark
ICON_NOTIFICATIONS :: "\xEE\x9F\xB4" // U+E7F4 notifications
ICON_EXPAND_MORE :: "\xEE\x97\x8F" // U+E5CF expand_more