diff --git a/frontend/src/components/react/AssetsButton.tsx b/frontend/src/components/react/AssetsButton.tsx
index 2b507ec..22b0165 100644
--- a/frontend/src/components/react/AssetsButton.tsx
+++ b/frontend/src/components/react/AssetsButton.tsx
@@ -8,7 +8,7 @@ interface Props {
}
export default function AssetsButton({
- className = 'inline-flex items-center gap-2 bg-surface0 hover:bg-surface1 text-subtext1 hover:text-text px-3 py-2 rounded-lg border border-surface1 transition-colors text-sm',
+ className = 'btn btn--ghost',
label = 'Assets',
iconSize = 14,
}: Props) {
@@ -89,8 +89,7 @@ export default function AssetsButton({
type="button"
onClick={() => setOpen(false)}
aria-label="Close"
- className="p-1.5 text-[var(--subtext0)] hover:text-[var(--text)] hover:bg-[var(--surface0)] transition-colors"
- style={{ borderRadius: 2 }}
+ className="btn btn--ghost btn--icon btn--sm"
>
- ↶ Return to the catalogue
+ ↶ Return to the catalogue
diff --git a/frontend/src/pages/index.astro b/frontend/src/pages/index.astro
index b8d95a0..9866d90 100644
--- a/frontend/src/pages/index.astro
+++ b/frontend/src/pages/index.astro
@@ -96,16 +96,16 @@ const isAdmin = Astro.cookies.get('admin_session')?.value === '1';
{isAdmin && (
)}
diff --git a/frontend/src/pages/posts/[slug].astro b/frontend/src/pages/posts/[slug].astro
index bd4c80e..0ce26b7 100644
--- a/frontend/src/pages/posts/[slug].astro
+++ b/frontend/src/pages/posts/[slug].astro
@@ -84,7 +84,7 @@ const displayTitle = post ? (post.title || formatSlug(post.slug)) : 'Work';
)}
@@ -99,7 +99,7 @@ const displayTitle = post ? (post.title || formatSlug(post.slug)) : 'Work';
{isAdmin && (
-
+
Edit
diff --git a/frontend/src/styles/global.css b/frontend/src/styles/global.css
index 71d1860..bc01371 100644
--- a/frontend/src/styles/global.css
+++ b/frontend/src/styles/global.css
@@ -914,58 +914,98 @@ code, pre, kbd, samp {
0 14px 40px -24px rgba(0, 0, 0, 0.9);
}
-/* ───── Buttons ───── */
-.btn-stamp {
+/* ───── Buttons — one system ─────
+ * Base .btn = layout + size + focus/disabled. One variant for color
+ * (--primary / --ghost / --danger), one size modifier (--sm / --lg),
+ * shape modifiers (--icon / --block). Never restyle buttons ad-hoc. */
+.btn {
display: inline-flex;
align-items: center;
+ justify-content: center;
gap: 0.5rem;
+ height: 2.5rem;
+ padding: 0 1.2rem;
font-family: var(--font-display);
font-style: italic;
font-weight: 500;
font-size: 0.95rem;
- padding: 0.55rem 1.2rem;
+ line-height: 1;
+ letter-spacing: 0.02em;
+ background: transparent;
+ color: var(--text);
+ border: 1px solid transparent;
+ border-radius: 1px;
+ text-decoration: none;
+ white-space: nowrap;
+ cursor: pointer;
+ transition: transform 0.15s ease, background 0.15s ease, color 0.15s ease,
+ border-color 0.15s ease, box-shadow 0.15s ease;
+}
+.btn:focus-visible {
+ outline: none;
+ border-color: var(--mauve);
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--mauve) 35%, transparent);
+}
+.btn:disabled,
+.btn[aria-disabled="true"] {
+ opacity: 0.55;
+ cursor: default;
+ pointer-events: none;
+}
+.btn svg { width: 1.05em; height: 1.05em; flex-shrink: 0; }
+
+/* Variants */
+.btn--primary {
background: var(--mauve);
color: var(--rosewater);
- border: 1px solid var(--mauve);
- border-radius: 1px;
- letter-spacing: 0.02em;
- text-decoration: none;
- cursor: pointer;
- transition: transform 0.15s ease, background 0.15s ease, box-shadow 0.15s ease;
+ border-color: var(--mauve);
box-shadow: 0 4px 0 -2px color-mix(in srgb, var(--mauve) 60%, black);
}
-.btn-stamp:hover {
+.btn--primary:hover {
transform: translateY(-1px);
background: var(--red);
border-color: var(--red);
box-shadow: 0 6px 0 -2px color-mix(in srgb, var(--red) 60%, black);
}
-.btn-stamp:active {
+.btn--primary:active {
transform: translateY(1px);
box-shadow: 0 1px 0 -1px color-mix(in srgb, var(--mauve) 60%, black);
}
-.btn-ghost {
- display: inline-flex;
- align-items: center;
- gap: 0.5rem;
- font-family: var(--font-sans);
- font-size: 0.82rem;
- padding: 0.4rem 0.85rem;
- background: transparent;
+.btn--ghost {
color: var(--subtext1);
- border: 1px solid var(--surface2);
- border-radius: 1px;
- letter-spacing: 0.06em;
- text-transform: uppercase;
- text-decoration: none;
- cursor: pointer;
- transition: color 0.15s, border-color 0.15s, background 0.15s;
+ border-color: var(--surface2);
+ background: color-mix(in srgb, var(--surface0) 45%, transparent);
}
-.btn-ghost:hover {
+.btn--ghost:hover {
color: var(--mauve);
- border-color: var(--mauve);
- background: color-mix(in srgb, var(--mauve) 8%, transparent);
+ border-color: color-mix(in srgb, var(--mauve) 50%, var(--surface2));
+ background: color-mix(in srgb, var(--surface0) 80%, transparent);
}
+.btn--danger {
+ color: var(--red);
+ border-color: color-mix(in srgb, var(--red) 40%, var(--surface2));
+ background: color-mix(in srgb, var(--surface0) 45%, transparent);
+}
+.btn--danger:hover {
+ color: var(--rosewater);
+ background: var(--red);
+ border-color: var(--red);
+}
+/* Pressed/selected state for toggle & tab buttons */
+.btn.is-active {
+ color: var(--mauve);
+ border-color: color-mix(in srgb, var(--mauve) 55%, var(--surface2));
+ background: color-mix(in srgb, var(--mauve) 14%, transparent);
+}
+
+/* Sizes */
+.btn--sm { height: 2rem; padding: 0 0.85rem; font-size: 0.85rem; gap: 0.35rem; }
+.btn--lg { height: 3rem; padding: 0 1.6rem; font-size: 1.05rem; }
+
+/* Shapes */
+.btn--icon { padding: 0; width: 2.5rem; }
+.btn--icon.btn--sm { width: 2rem; }
+.btn--block { width: 100%; }
/* ───── Top-bar controls — one height, one language ───── */
.topbar-control {