diff --git a/src/components/ColorSwitcher.astro b/src/components/ColorSwitcher.astro
new file mode 100644
index 0000000..3c8c8fd
--- /dev/null
+++ b/src/components/ColorSwitcher.astro
@@ -0,0 +1,227 @@
+---
+import type { HTMLAttributes } from "astro/types";
+
+interface Palette { key: string; label: string; }
+
+interface Props extends HTMLAttributes<"div"> {
+ storageKey?: string;
+ rootSelector?: string; // where to set data-color (e.g., "html" or ".wrnx")
+ ui?: "swatches" | "menu"; // grid or dropdown
+ showLabels?: boolean; // for swatch grid
+ compact?: boolean; // smaller dots
+ trigger?: "default" | "dot"; // dot = only a colored circle in trigger
+ palettes?: Palette[]; // keys/labels to display
+}
+
+const {
+ storageKey = "wr-color",
+ rootSelector = "html",
+ ui = "menu",
+ showLabels = false,
+ compact = false,
+ trigger = "default",
+ class: className,
+ palettes = [
+ { key: "ocean", label: "Ocean" },
+ { key: "forest", label: "Forest" },
+ { key: "sunset", label: "Sunset" },
+ { key: "grape", label: "Grape" },
+ { key: "slate", label: "Slate" },
+ { key: "mint", label: "Mint" },
+ { key: "amber", label: "Amber" },
+ { key: "crimson", label: "Crimson" },
+ { key: "indigo", label: "Indigo" },
+ ],
+ ...rest
+}: Props = Astro.props;
+
+const dot = compact ? "wr:h-3.5 wr:w-3.5" : "wr:h-4 wr:w-4";
+const btn =
+ "wr:inline-flex wr:items-center wr:gap-2 wr:rounded-xl wr:border wr:border-border wr:bg-background/80 wr:backdrop-blur-sm " +
+ "wr:text-foreground wr:h-10 wr:px-3 wr:text-sm wr:shadow-sm wr:transition wr:duration-200 wr:ease-out " +
+ "wr:hover:bg-muted/70 wr:focus-visible:outline-none wr:focus-visible:ring-2 wr:focus-visible:ring-primary/40 wr:focus-visible:ring-offset-2";
+const iconBtn =
+ "wr:inline-flex wr:items-center wr:justify-center wr:h-9 wr:w-9 wr:rounded-xl wr:border wr:border-border wr:bg-background/80 wr:backdrop-blur-sm " +
+ "wr:text-foreground wr:shadow-sm wr:transition wr:duration-200 wr:ease-out wr:hover:bg-muted/70 " +
+ "wr:focus-visible:outline-none wr:focus-visible:ring-2 wr:focus-visible:ring-primary/40 wr:focus-visible:ring-offset-2";
+const item =
+ "wr:flex wr:items-center wr:gap-3 wr:w-full wr:px-3 wr:py-2.5 wr:rounded-lg wr:text-sm wr:hover:bg-muted/70 wr:transition wr:duration-150";
+---
+
+{ui === "swatches" ? (
+
+ {palettes.map(p => (
+
+ ))}
+
+) : (
+
+ {trigger === "default" ? (
+
+ ) : (
+
+ )}
+
+
+ {palettes.map(p => (
+
+ ))}
+
+
+)}
+
+
+
diff --git a/src/components/ThemeSwitcher.astro b/src/components/ThemeSwitcher.astro
new file mode 100644
index 0000000..e3152bd
--- /dev/null
+++ b/src/components/ThemeSwitcher.astro
@@ -0,0 +1,322 @@
+---
+import type { HTMLAttributes } from "astro/types";
+import { Icon } from "astro-icon/components";
+
+interface Props extends HTMLAttributes<"div"> {
+ ui?: "icons" | "menu";
+ storageKey?: string;
+ rootSelector?: string;
+ label?: string;
+ compact?: boolean;
+ darkBgClass?: string[];
+ lightBgClass?: string[];
+ rootExtraClass?: string;
+}
+
+const {
+ ui = "menu",
+ storageKey = "wr-theme",
+ rootSelector = "html",
+ label = "",
+ compact = false,
+ darkBgClass = ["wr-theme-dark-soft"],
+ lightBgClass = ["wr-theme-light"],
+ rootExtraClass = "",
+ class: className,
+ ...rest
+}: Props = Astro.props;
+
+const btn =
+ "wr:inline-flex wr:items-center wr:gap-2 wr:rounded-xl wr:border wr:border-border wr:bg-background/80 wr:backdrop-blur-sm " +
+ "wr:text-foreground wr:h-10 wr:px-3 wr:text-sm wr:shadow-sm wr:transition wr:duration-200 wr:ease-out " +
+ "wr:hover:bg-muted/70 wr:focus-visible:outline-none wr:focus-visible:ring-2 wr:focus-visible:ring-primary/40 wr:focus-visible:ring-offset-2";
+
+const iconBtn =
+ "wr:inline-flex wr:items-center wr:justify-center wr:h-9 wr:w-9 wr:rounded-xl wr:border wr:border-border wr:bg-background/80 wr:backdrop-blur-sm " +
+ "wr:text-foreground wr:shadow-sm wr:transition wr:duration-200 wr:ease-out wr:hover:bg-muted/70 " +
+ "wr:focus-visible:outline-none wr:focus-visible:ring-2 wr:focus-visible:ring-primary/40 wr:focus-visible:ring-offset-2";
+
+const item =
+ "wr:flex wr:items-center wr:gap-3 wr:w-full wr:px-3 wr:py-2.5 wr:rounded-lg wr:text-sm wr:hover:bg-muted/70 wr:transition wr:duration-150";
+
+const icon = "wr:size-4";
+const checkBase = "wr:size-4 wr:opacity-0 wr:scale-90 wr:transition";
+---
+
+
+ {label &&
{label}}
+
+ {
+ ui === "icons" && (
+
+
+
+
+
+ )
+ }
+
+ {
+ ui === "menu" && (
+
+
+
+
+
+
+
+
+
+ )
+ }
+
+
+
+
+
diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro
index fbf252f..211069c 100644
--- a/src/layouts/Base.astro
+++ b/src/layouts/Base.astro
@@ -5,7 +5,7 @@ import { cn } from "../utils/cn";
const attributes = Astro.props;
---
-
+
diff --git a/src/pages/buttons.astro b/src/pages/buttons.astro
index 0f03911..4071aa4 100644
--- a/src/pages/buttons.astro
+++ b/src/pages/buttons.astro
@@ -1,14 +1,24 @@
---
import Base from "../layouts/Base.astro";
import Button from "../components/Button.astro";
+import ThemeSwitcher from "../components/ThemeSwitcher.astro";
+import ColorSwitcher from "../components/ColorSwitcher.astro";
---
+
+
+