Packages

🍩 Lustre Bindings to Oat UI Components

Current section

Files

Jump to
glaze_oat src glaze@oat@theme.erl
Raw

src/glaze@oat@theme.erl

-module(glaze@oat@theme).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glaze/oat/theme.gleam").
-export([from_list/1, default_theme/0, to_list/1, get/2, set/3, set_many/2, set_colors/23, style_tag_with_color_scheme/2, style_tag/1]).
-export_type([token/0, theme/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Theme tokens and \\<style\\> generation for Oat UI.\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" import glaze/oat\n"
" import glaze/oat/theme\n"
"\n"
" let my_theme =\n"
" theme.default_theme()\n"
" |> theme.set(theme.Primary, \"light-dark(#334155, #e2e8f0)\")\n"
" |> theme.set(theme.RadiusLarge, \"0.875rem\")\n"
"\n"
" html.head([], [\n"
" glaze_oat.register(glaze_oat.version),\n"
" theme.style_tag(my_theme),\n"
" ])\n"
" ```\n"
"\n"
" ## CSS values\n"
"\n"
" Any valid CSS value may be used (`#hex`, `rgb()`, `var(...)`, `clamp(...)`,\n"
" `light-dark(...)`, unit values such as `rem`, etc).\n"
"\n"
).
-type token() :: background |
foreground |
card |
card_foreground |
primary |
primary_foreground |
secondary |
secondary_foreground |
muted |
muted_foreground |
faint |
faint_foreground |
accent |
danger |
danger_foreground |
success |
success_foreground |
warning |
warning_foreground |
border |
input |
ring |
space1 |
space2 |
space3 |
space4 |
space5 |
space6 |
space8 |
space10 |
space12 |
space14 |
space16 |
space18 |
radius_small |
radius_medium |
radius_large |
radius_full |
bar_height |
font_sans |
font_mono |
text1 |
text2 |
text3 |
text4 |
text5 |
text6 |
text7 |
text8 |
text_regular |
leading_normal |
font_normal |
font_medium |
font_semibold |
font_bold |
shadow_small |
shadow_medium |
shadow_large |
transition_fast |
transition |
z_dropdown |
z_modal.
-opaque theme() :: {theme, gleam@dict:dict(token(), binary())}.
-file("src/glaze/oat/theme.gleam", 201).
?DOC(
" Construct a `Theme` from a list of token-value pairs.\n"
"\n"
" If the same token appears multiple times, the last value overrides previous ones.\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" let t = theme.from_list([\n"
" #(theme.Primary, \"oklch(0.205 0 0)\"),\n"
" #(theme.PrimaryForeground, \"oklch(0.985 0 0)\"),\n"
" ])\n"
" ```\n"
).
-spec from_list(list({token(), binary()})) -> theme().
from_list(Tokens) ->
Values = gleam@list:fold(
Tokens,
maps:new(),
fun(Acc, Cur) ->
{Token, Value} = Cur,
gleam@dict:insert(Acc, Token, Value)
end
),
{theme, Values}.
-file("src/glaze/oat/theme.gleam", 114).
?DOC(
" Returns Oats default theme.\n"
"\n"
" This includes sensible defaults for spacing, radius, typography, shadows, transitions, and z-index values.\n"
"\n"
" Start with this, even if you want to later change all of the colors.\n"
).
-spec default_theme() -> theme().
default_theme() ->
_pipe = [{background, <<"light-dark(#fff, #09090b)"/utf8>>},
{foreground, <<"light-dark(#09090b, #fafafa)"/utf8>>},
{card, <<"light-dark(#fff, #18181b)"/utf8>>},
{card_foreground, <<"light-dark(#09090b, #fafafa)"/utf8>>},
{primary, <<"light-dark(#574747, #fafafa)"/utf8>>},
{primary_foreground, <<"light-dark(#fafafa, #18181b)"/utf8>>},
{secondary, <<"light-dark(#f4f4f5, #27272a)"/utf8>>},
{secondary_foreground, <<"light-dark(#574747, #fafafa)"/utf8>>},
{muted, <<"light-dark(#f4f4f5, #27272a)"/utf8>>},
{muted_foreground, <<"light-dark(#71717a, #a1a1aa)"/utf8>>},
{faint, <<"light-dark(#fafafa, #1e1e21)"/utf8>>},
{faint_foreground, <<"light-dark(#a1a1aa, #71717a)"/utf8>>},
{accent, <<"light-dark(#f4f4f5, #27272a)"/utf8>>},
{danger, <<"light-dark(#d32f2f, #f4807b)"/utf8>>},
{danger_foreground, <<"light-dark(#fafafa, #18181b)"/utf8>>},
{success, <<"light-dark(#008032, #6cc070)"/utf8>>},
{success_foreground, <<"light-dark(#fafafa, #18181b)"/utf8>>},
{warning, <<"light-dark(#a65b00, #f0a030)"/utf8>>},
{warning_foreground, <<"#09090b"/utf8>>},
{border, <<"light-dark(#d4d4d8, #52525b)"/utf8>>},
{input, <<"light-dark(#d4d4d8, #52525b)"/utf8>>},
{ring, <<"light-dark(#574747, #d4d4d8)"/utf8>>},
{space1, <<"0.25rem"/utf8>>},
{space2, <<"0.5rem"/utf8>>},
{space3, <<"0.75rem"/utf8>>},
{space4, <<"1rem"/utf8>>},
{space5, <<"1.25rem"/utf8>>},
{space6, <<"1.5rem"/utf8>>},
{space8, <<"2rem"/utf8>>},
{space10, <<"2.5rem"/utf8>>},
{space12, <<"3rem"/utf8>>},
{space14, <<"3.5rem"/utf8>>},
{space16, <<"4rem"/utf8>>},
{space18, <<"4.5rem"/utf8>>},
{radius_small, <<"0.125rem"/utf8>>},
{radius_medium, <<"0.375rem"/utf8>>},
{radius_large, <<"0.75rem"/utf8>>},
{radius_full, <<"9999px"/utf8>>},
{bar_height, <<"0.5rem"/utf8>>},
{font_sans, <<"system-ui, sans-serif"/utf8>>},
{font_mono, <<"ui-monospace, Consolas, monospace"/utf8>>},
{text1, <<"clamp(1.75rem, 1.5rem + 1.1vw, 2.25rem)"/utf8>>},
{text2, <<"clamp(1.5rem, 1.3rem + 0.8vw, 1.875rem)"/utf8>>},
{text3, <<"clamp(1.25rem, 1.1rem + 0.5vw, 1.5rem)"/utf8>>},
{text4, <<"clamp(1.125rem, 1.05rem + 0.3vw, 1.25rem)"/utf8>>},
{text5, <<"1.125rem"/utf8>>},
{text6, <<"1rem"/utf8>>},
{text7, <<"0.875rem"/utf8>>},
{text8, <<"0.75rem"/utf8>>},
{text_regular, <<"var(--text-6)"/utf8>>},
{leading_normal, <<"1.5"/utf8>>},
{font_normal, <<"400"/utf8>>},
{font_medium, <<"500"/utf8>>},
{font_semibold, <<"600"/utf8>>},
{font_bold, <<"600"/utf8>>},
{shadow_small, <<"0 1px 2px 0 rgb(0 0 0 / 0.05)"/utf8>>},
{shadow_medium,
<<"0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)"/utf8>>},
{shadow_large,
<<"0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)"/utf8>>},
{transition_fast, <<"120ms cubic-bezier(0.4, 0, 0.2, 1)"/utf8>>},
{transition, <<"200ms cubic-bezier(0.4, 0, 0.2, 1)"/utf8>>},
{z_dropdown, <<"50"/utf8>>},
{z_modal, <<"200"/utf8>>}],
from_list(_pipe).
-file("src/glaze/oat/theme.gleam", 212).
?DOC(" Convert a `Theme` into token-value pairs.\n").
-spec to_list(theme()) -> list({token(), binary()}).
to_list(Theme) ->
{theme, Values} = Theme,
maps:to_list(Values).
-file("src/glaze/oat/theme.gleam", 217).
-spec get(theme(), token()) -> binary().
get(Theme, Token) ->
{theme, Values} = Theme,
case gleam_stdlib:map_get(Values, Token) of
{ok, Value} ->
Value;
{error, _} ->
<<""/utf8>>
end.
-file("src/glaze/oat/theme.gleam", 225).
-spec set(theme(), token(), binary()) -> theme().
set(Theme, Token, Value) ->
{theme, Values} = Theme,
{theme, gleam@dict:insert(Values, Token, Value)}.
-file("src/glaze/oat/theme.gleam", 230).
-spec set_many(theme(), list({token(), binary()})) -> theme().
set_many(Theme, Updates) ->
gleam@list:fold(
Updates,
Theme,
fun(Acc, Cur) ->
{Token, Value} = Cur,
set(Acc, Token, Value)
end
).
-file("src/glaze/oat/theme.gleam", 273).
?DOC(
" Function to update all color-related tokens in one call.\n"
"\n"
" This is useful when defining a complete palette while keeping spacing,\n"
" typography, and motion defaults unchanged.\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" let palette =\n"
" theme.default_theme()\n"
" |> theme.set_colors(\n"
" background: \"light-dark(#ffffff, #09090b)\",\n"
" foreground: \"light-dark(#09090b, #fafafa)\",\n"
" card: \"light-dark(#ffffff, #18181b)\",\n"
" card_foreground: \"light-dark(#09090b, #fafafa)\",\n"
" primary: \"light-dark(#334155, #e2e8f0)\",\n"
" primary_foreground: \"light-dark(#ffffff, #0f172a)\",\n"
" secondary: \"light-dark(#f1f5f9, #1f2937)\",\n"
" secondary_foreground: \"light-dark(#0f172a, #f8fafc)\",\n"
" muted: \"light-dark(#f8fafc, #27272a)\",\n"
" muted_foreground: \"light-dark(#64748b, #a1a1aa)\",\n"
" faint: \"light-dark(#fafafa, #1e1e21)\",\n"
" faint_foreground: \"light-dark(#a1a1aa, #71717a)\",\n"
" accent: \"light-dark(#e2e8f0, #334155)\",\n"
" danger: \"light-dark(#d32f2f, #f4807b)\",\n"
" danger_foreground: \"light-dark(#fafafa, #18181b)\",\n"
" success: \"light-dark(#008032, #6cc070)\",\n"
" success_foreground: \"light-dark(#fafafa, #18181b)\",\n"
" warning: \"light-dark(#a65b00, #f0a030)\",\n"
" warning_foreground: \"#09090b\",\n"
" border: \"light-dark(#d4d4d8, #52525b)\",\n"
" input: \"light-dark(#d4d4d8, #52525b)\",\n"
" ring: \"light-dark(#574747, #d4d4d8)\",\n"
" )\n"
" ```\n"
).
-spec set_colors(
theme(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary(),
binary()
) -> theme().
set_colors(
Theme,
Background,
Foreground,
Card,
Card_foreground,
Primary,
Primary_foreground,
Secondary,
Secondary_foreground,
Muted,
Muted_foreground,
Faint,
Faint_foreground,
Accent,
Danger,
Danger_foreground,
Success,
Success_foreground,
Warning,
Warning_foreground,
Border,
Input,
Ring
) ->
set_many(
Theme,
[{background, Background},
{foreground, Foreground},
{card, Card},
{card_foreground, Card_foreground},
{primary, Primary},
{primary_foreground, Primary_foreground},
{secondary, Secondary},
{secondary_foreground, Secondary_foreground},
{muted, Muted},
{muted_foreground, Muted_foreground},
{faint, Faint},
{faint_foreground, Faint_foreground},
{accent, Accent},
{danger, Danger},
{danger_foreground, Danger_foreground},
{success, Success},
{success_foreground, Success_foreground},
{warning, Warning},
{warning_foreground, Warning_foreground},
{border, Border},
{input, Input},
{ring, Ring}]
).
-file("src/glaze/oat/theme.gleam", 373).
-spec token_css_var(token()) -> binary().
token_css_var(Token) ->
case Token of
background ->
<<"--background"/utf8>>;
foreground ->
<<"--foreground"/utf8>>;
card ->
<<"--card"/utf8>>;
card_foreground ->
<<"--card-foreground"/utf8>>;
primary ->
<<"--primary"/utf8>>;
primary_foreground ->
<<"--primary-foreground"/utf8>>;
secondary ->
<<"--secondary"/utf8>>;
secondary_foreground ->
<<"--secondary-foreground"/utf8>>;
muted ->
<<"--muted"/utf8>>;
muted_foreground ->
<<"--muted-foreground"/utf8>>;
faint ->
<<"--faint"/utf8>>;
faint_foreground ->
<<"--faint-foreground"/utf8>>;
accent ->
<<"--accent"/utf8>>;
danger ->
<<"--danger"/utf8>>;
danger_foreground ->
<<"--danger-foreground"/utf8>>;
success ->
<<"--success"/utf8>>;
success_foreground ->
<<"--success-foreground"/utf8>>;
warning ->
<<"--warning"/utf8>>;
warning_foreground ->
<<"--warning-foreground"/utf8>>;
border ->
<<"--border"/utf8>>;
input ->
<<"--input"/utf8>>;
ring ->
<<"--ring"/utf8>>;
space1 ->
<<"--space-1"/utf8>>;
space2 ->
<<"--space-2"/utf8>>;
space3 ->
<<"--space-3"/utf8>>;
space4 ->
<<"--space-4"/utf8>>;
space5 ->
<<"--space-5"/utf8>>;
space6 ->
<<"--space-6"/utf8>>;
space8 ->
<<"--space-8"/utf8>>;
space10 ->
<<"--space-10"/utf8>>;
space12 ->
<<"--space-12"/utf8>>;
space14 ->
<<"--space-14"/utf8>>;
space16 ->
<<"--space-16"/utf8>>;
space18 ->
<<"--space-18"/utf8>>;
radius_small ->
<<"--radius-small"/utf8>>;
radius_medium ->
<<"--radius-medium"/utf8>>;
radius_large ->
<<"--radius-large"/utf8>>;
radius_full ->
<<"--radius-full"/utf8>>;
bar_height ->
<<"--bar-height"/utf8>>;
font_sans ->
<<"--font-sans"/utf8>>;
font_mono ->
<<"--font-mono"/utf8>>;
text1 ->
<<"--text-1"/utf8>>;
text2 ->
<<"--text-2"/utf8>>;
text3 ->
<<"--text-3"/utf8>>;
text4 ->
<<"--text-4"/utf8>>;
text5 ->
<<"--text-5"/utf8>>;
text6 ->
<<"--text-6"/utf8>>;
text7 ->
<<"--text-7"/utf8>>;
text8 ->
<<"--text-8"/utf8>>;
text_regular ->
<<"--text-regular"/utf8>>;
leading_normal ->
<<"--leading-normal"/utf8>>;
font_normal ->
<<"--font-normal"/utf8>>;
font_medium ->
<<"--font-medium"/utf8>>;
font_semibold ->
<<"--font-semibold"/utf8>>;
font_bold ->
<<"--font-bold"/utf8>>;
shadow_small ->
<<"--shadow-small"/utf8>>;
shadow_medium ->
<<"--shadow-medium"/utf8>>;
shadow_large ->
<<"--shadow-large"/utf8>>;
transition_fast ->
<<"--transition-fast"/utf8>>;
transition ->
<<"--transition"/utf8>>;
z_dropdown ->
<<"--z-dropdown"/utf8>>;
z_modal ->
<<"--z-modal"/utf8>>
end.
-file("src/glaze/oat/theme.gleam", 355).
-spec theme_to_css_variables(theme()) -> binary().
theme_to_css_variables(Theme) ->
{theme, Values} = Theme,
_pipe = Values,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
{Token_a, _} = A,
{Token_b, _} = B,
gleam@string:compare(token_css_var(Token_a), token_css_var(Token_b))
end
),
gleam@list:fold(
_pipe@2,
<<""/utf8>>,
fun(Acc, Cur) ->
{Token, Value} = Cur,
case Value of
<<""/utf8>> ->
Acc;
_ ->
<<<<<<<<<<Acc/binary, "\n"/utf8>>/binary,
(token_css_var(Token))/binary>>/binary,
": "/utf8>>/binary,
Value/binary>>/binary,
";"/utf8>>
end
end
).
-file("src/glaze/oat/theme.gleam", 341).
?DOC(
" Render a `<style>` tag that sets all CSS variables from the theme with a custom `color-scheme`.\n"
"\n"
" For a full list of options for `color_scheme` refer to https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/color-scheme\n"
"\n"
" ### Example\n"
"\n"
" ```gleam\n"
" theme.style_tag_with_color_scheme(light_theme, \"light\")\n"
" theme.style_tag_with_color_scheme(dark_theme, \"dark\")\n"
" ```\n"
).
-spec style_tag_with_color_scheme(theme(), binary()) -> lustre@vdom@vnode:element(any()).
style_tag_with_color_scheme(Theme, Color_scheme) ->
lustre@element@html:style(
[],
<<<<<<<<":root { color-scheme: "/utf8, Color_scheme/binary>>/binary,
"; "/utf8>>/binary,
(theme_to_css_variables(Theme))/binary>>/binary,
"}"/utf8>>
).
-file("src/glaze/oat/theme.gleam", 326).
?DOC(" Render a `<style>` tag that sets all CSS variables from the theme.\n").
-spec style_tag(theme()) -> lustre@vdom@vnode:element(any()).
style_tag(Theme) ->
style_tag_with_color_scheme(Theme, <<"light dark"/utf8>>).