-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 \\ 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", 200). ?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", 113). ?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", 211). ?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", 216). -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", 224). -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", 229). -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", 272). ?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", 367). -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", 354). -spec theme_to_css_variables(theme()) -> binary(). theme_to_css_variables(Theme) -> {theme, Values} = Theme, _pipe = Values, _pipe@1 = maps:to_list(_pipe), gleam@list:fold( _pipe@1, <<""/utf8>>, fun(Acc, Cur) -> {Token, Value} = Cur, case Value of <<""/utf8>> -> Acc; _ -> <<<<<<<<<>/binary, (token_css_var(Token))/binary>>/binary, ": "/utf8>>/binary, Value/binary>>/binary, ";"/utf8>> end end ). -file("src/glaze_oat/theme.gleam", 340). ?DOC( " Render a `