init elas atelier #1
@@ -8,7 +8,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function AssetsButton({
|
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',
|
label = 'Assets',
|
||||||
iconSize = 14,
|
iconSize = 14,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
@@ -89,8 +89,7 @@ export default function AssetsButton({
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => setOpen(false)}
|
onClick={() => setOpen(false)}
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
className="p-1.5 text-[var(--subtext0)] hover:text-[var(--text)] hover:bg-[var(--surface0)] transition-colors"
|
className="btn btn--ghost btn--icon btn--sm"
|
||||||
style={{ borderRadius: 2 }}
|
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ export default function ContactForm() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={status === 'sending'}
|
disabled={status === 'sending'}
|
||||||
className="btn-stamp disabled:opacity-60"
|
className="btn btn--primary"
|
||||||
>
|
>
|
||||||
{status === 'sending' ? 'Sending…' : 'Send message'}
|
{status === 'sending' ? 'Sending…' : 'Send message'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export default function DeletePostButton({ slug, title, variant = 'full' }: Prop
|
|||||||
disabled={busy}
|
disabled={busy}
|
||||||
title="Delete post"
|
title="Delete post"
|
||||||
aria-label="Delete post"
|
aria-label="Delete post"
|
||||||
className="p-2 rounded-md bg-surface0/80 hover:bg-red/20 text-subtext0 hover:text-red border border-surface1 transition-colors disabled:opacity-50"
|
className="btn btn--danger btn--icon btn--sm"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@@ -60,7 +60,7 @@ export default function DeletePostButton({ slug, title, variant = 'full' }: Prop
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
className="inline-flex items-center gap-2 bg-surface0 hover:bg-red/15 text-subtext1 hover:text-red px-3 py-1.5 md:px-4 md:py-2 rounded-md border border-surface1 hover:border-red/30 transition-colors text-sm disabled:opacity-50"
|
className="btn btn--danger btn--sm"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
|||||||
@@ -197,8 +197,7 @@ export default function PostList({ posts: initialPosts, isAdmin = false }: Props
|
|||||||
onClick={e => e.stopPropagation()}
|
onClick={e => e.stopPropagation()}
|
||||||
title="Edit"
|
title="Edit"
|
||||||
aria-label={`Edit ${displayTitle}`}
|
aria-label={`Edit ${displayTitle}`}
|
||||||
className="p-1.5 bg-[var(--mantle)] text-[var(--rosewater)] hover:bg-[var(--blue)] border border-[var(--surface2)] transition-colors"
|
className="btn btn--ghost btn--icon btn--sm"
|
||||||
style={{ borderRadius: 1 }}
|
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" /><path d="m15 5 4 4" /></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" /><path d="m15 5 4 4" /></svg>
|
||||||
</a>
|
</a>
|
||||||
@@ -208,8 +207,7 @@ export default function PostList({ posts: initialPosts, isAdmin = false }: Props
|
|||||||
disabled={isDeleting}
|
disabled={isDeleting}
|
||||||
title="Remove"
|
title="Remove"
|
||||||
aria-label={`Remove ${displayTitle}`}
|
aria-label={`Remove ${displayTitle}`}
|
||||||
className="p-1.5 bg-[var(--mantle)] text-[var(--rosewater)] hover:bg-[var(--red)] border border-[var(--surface2)] transition-colors disabled:opacity-50"
|
className="btn btn--danger btn--icon btn--sm"
|
||||||
style={{ borderRadius: 1 }}
|
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /><line x1="10" x2="10" y1="11" y2="17" /><line x1="14" x2="14" y1="11" y2="17" /></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18" /><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /><line x1="10" x2="10" y1="11" y2="17" /><line x1="14" x2="14" y1="11" y2="17" /></svg>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -109,8 +109,7 @@ export default function AssetManager({ mode = 'manage', onSelect }: Props) {
|
|||||||
{mode === 'select' && onSelect && (
|
{mode === 'select' && onSelect && (
|
||||||
<button
|
<button
|
||||||
onClick={() => onSelect(asset)}
|
onClick={() => onSelect(asset)}
|
||||||
className="bg-[var(--mauve)] hover:bg-[var(--red)] text-[var(--rosewater)] px-4 py-2 text-sm font-display italic font-medium transition-colors shadow-lg inline-flex items-center gap-1.5"
|
className="btn btn--primary btn--sm"
|
||||||
style={{ borderRadius: 1 }}
|
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14"/><path d="m12 5 7 7-7 7"/></svg>
|
||||||
Insert
|
Insert
|
||||||
@@ -118,8 +117,7 @@ export default function AssetManager({ mode = 'manage', onSelect }: Props) {
|
|||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={() => handleDelete(asset.name)}
|
onClick={() => handleDelete(asset.name)}
|
||||||
className="bg-[var(--red)]/80 hover:bg-[var(--red)] text-[var(--rosewater)] p-2.5 transition-colors shadow-lg"
|
className="btn btn--danger btn--icon btn--sm"
|
||||||
style={{ borderRadius: 1 }}
|
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -415,11 +415,11 @@ export default function Editor({ editSlug }: Props) {
|
|||||||
{/* Actions bar */}
|
{/* Actions bar */}
|
||||||
<div className="flex flex-wrap gap-4 mb-6">
|
<div className="flex flex-wrap gap-4 mb-6">
|
||||||
{originalSlug && (
|
{originalSlug && (
|
||||||
<button onClick={handleDelete} className="btn-ghost py-3 px-6 text-[var(--red)] border-[var(--red)]/40 hover:bg-[var(--red)]/10 hover:border-[var(--red)] hover:text-[var(--red)]">
|
<button onClick={handleDelete} className="btn btn--danger">
|
||||||
Remove
|
Remove
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button onClick={handleSave} className="btn-stamp py-3 px-8 whitespace-nowrap">
|
<button onClick={handleSave} className="btn btn--primary">
|
||||||
Save work
|
Save work
|
||||||
</button>
|
</button>
|
||||||
{originalSlug && (
|
{originalSlug && (
|
||||||
@@ -427,7 +427,7 @@ export default function Editor({ editSlug }: Props) {
|
|||||||
href={`/posts/${encodeURIComponent(originalSlug)}`}
|
href={`/posts/${encodeURIComponent(originalSlug)}`}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className="btn-ghost py-3 px-6 inline-flex items-center justify-center gap-2 whitespace-nowrap"
|
className="btn btn--ghost"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg>
|
||||||
View work
|
View work
|
||||||
@@ -521,12 +521,7 @@ export default function Editor({ editSlug }: Props) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setVimEnabled(v => !v)}
|
onClick={() => setVimEnabled(v => !v)}
|
||||||
className={`text-xs px-3 py-1.5 border transition-colors font-mono ${
|
className={`btn btn--sm${vimEnabled ? ' is-active' : ''}`}
|
||||||
vimEnabled
|
|
||||||
? 'bg-[var(--mauve)]/20 text-[var(--mauve)] border-[var(--mauve)]/30'
|
|
||||||
: 'bg-[var(--surface0)] text-[var(--subtext0)] border-[var(--surface2)] hover:text-[var(--text)]'
|
|
||||||
}`}
|
|
||||||
style={{ borderRadius: 2 }}
|
|
||||||
title={vimEnabled ? 'Vim mode ON' : 'Vim mode OFF'}
|
title={vimEnabled ? 'Vim mode ON' : 'Vim mode OFF'}
|
||||||
>
|
>
|
||||||
{vimEnabled ? 'VIM' : 'vim'}
|
{vimEnabled ? 'VIM' : 'vim'}
|
||||||
@@ -534,19 +529,14 @@ export default function Editor({ editSlug }: Props) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowPreview(p => !p)}
|
onClick={() => setShowPreview(p => !p)}
|
||||||
className={`text-xs px-3 py-1.5 border transition-colors ${
|
className={`btn btn--sm${showPreview ? ' is-active' : ''}`}
|
||||||
showPreview
|
|
||||||
? 'bg-[var(--blue)]/20 text-[var(--blue)] border-[var(--blue)]/30'
|
|
||||||
: 'bg-[var(--surface0)] text-[var(--subtext0)] border-[var(--surface2)] hover:text-[var(--text)]'
|
|
||||||
}`}
|
|
||||||
style={{ borderRadius: 2 }}
|
|
||||||
>
|
>
|
||||||
{showPreview ? 'Hide Preview' : 'Show Preview'}
|
{showPreview ? 'Hide Preview' : 'Show Preview'}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setShowModal(true)}
|
onClick={() => setShowModal(true)}
|
||||||
className="btn-stamp text-sm py-1.5 px-4"
|
className="btn btn--primary btn--sm"
|
||||||
title="Insert an image — also: drag an image into the editor, or paste from clipboard"
|
title="Insert an image — also: drag an image into the editor, or paste from clipboard"
|
||||||
>
|
>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>
|
||||||
@@ -563,11 +553,7 @@ export default function Editor({ editSlug }: Props) {
|
|||||||
role="tab"
|
role="tab"
|
||||||
aria-selected={mobileView === 'edit'}
|
aria-selected={mobileView === 'edit'}
|
||||||
onClick={() => setMobileView('edit')}
|
onClick={() => setMobileView('edit')}
|
||||||
className={`flex-1 text-xs px-3 py-2 rounded border transition-colors ${
|
className={`btn btn--sm flex-1${mobileView === 'edit' ? ' is-active' : ''}`}
|
||||||
mobileView === 'edit'
|
|
||||||
? 'bg-blue/20 text-blue border-blue/30'
|
|
||||||
: 'bg-surface0 text-subtext0 border-surface1 hover:text-text'
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
Edit
|
Edit
|
||||||
</button>
|
</button>
|
||||||
@@ -576,11 +562,7 @@ export default function Editor({ editSlug }: Props) {
|
|||||||
role="tab"
|
role="tab"
|
||||||
aria-selected={mobileView === 'preview'}
|
aria-selected={mobileView === 'preview'}
|
||||||
onClick={() => setMobileView('preview')}
|
onClick={() => setMobileView('preview')}
|
||||||
className={`flex-1 text-xs px-3 py-2 rounded border transition-colors ${
|
className={`btn btn--sm flex-1${mobileView === 'preview' ? ' is-active' : ''}`}
|
||||||
mobileView === 'preview'
|
|
||||||
? 'bg-blue/20 text-blue border-blue/30'
|
|
||||||
: 'bg-surface0 text-subtext0 border-surface1 hover:text-text'
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
Preview
|
Preview
|
||||||
</button>
|
</button>
|
||||||
@@ -679,7 +661,7 @@ export default function Editor({ editSlug }: Props) {
|
|||||||
<h2 className="font-display italic text-2xl md:text-3xl text-[var(--text)] leading-tight">Add image</h2>
|
<h2 className="font-display italic text-2xl md:text-3xl text-[var(--text)] leading-tight">Add image</h2>
|
||||||
<p className="text-xs text-[var(--subtext0)] font-display italic mt-1">Click an image to insert it. Drag new files in to upload.</p>
|
<p className="text-xs text-[var(--subtext0)] font-display italic mt-1">Click an image to insert it. Drag new files in to upload.</p>
|
||||||
</div>
|
</div>
|
||||||
<button onClick={closeAssetModal} className="p-2 text-[var(--subtext0)] hover:text-[var(--red)] transition-colors">
|
<button onClick={closeAssetModal} className="btn btn--ghost btn--icon btn--sm">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
|
||||||
</button>
|
</button>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ export default function Inbox() {
|
|||||||
{m.email && (
|
{m.email && (
|
||||||
<a
|
<a
|
||||||
href={`mailto:${m.email}${m.subject ? `?subject=${encodeURIComponent('Re: ' + m.subject)}` : ''}`}
|
href={`mailto:${m.email}${m.subject ? `?subject=${encodeURIComponent('Re: ' + m.subject)}` : ''}`}
|
||||||
className="chip chip-accent uppercase"
|
className="btn btn--ghost btn--sm"
|
||||||
>
|
>
|
||||||
Reply
|
Reply
|
||||||
</a>
|
</a>
|
||||||
@@ -128,7 +128,7 @@ export default function Inbox() {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => remove(m.id)}
|
onClick={() => remove(m.id)}
|
||||||
className="chip text-[var(--red)]"
|
className="btn btn--danger btn--sm"
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default function Login() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
className="btn-stamp w-full justify-center disabled:opacity-60 disabled:cursor-not-allowed"
|
className="btn btn--primary btn--block"
|
||||||
>
|
>
|
||||||
{busy ? 'Unlocking…' : 'Enter'}
|
{busy ? 'Unlocking…' : 'Enter'}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ export default function Settings() {
|
|||||||
<Field label="Footer text" value={config.footer || ''} onChange={v => update('footer', v)} />
|
<Field label="Footer text" value={config.footer || ''} onChange={v => update('footer', v)} />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<button type="submit" className="btn-stamp">
|
<button type="submit" className="btn btn--primary">
|
||||||
Save site settings
|
Save site settings
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ import Layout from '../layouts/Layout.astro';
|
|||||||
The room you reached for has either been re-hung, withdrawn,<br class="hidden md:block" />
|
The room you reached for has either been re-hung, withdrawn,<br class="hidden md:block" />
|
||||||
or never made it to the wall in the first place.
|
or never made it to the wall in the first place.
|
||||||
</p>
|
</p>
|
||||||
<a href="/" class="btn-stamp">↶ Return to the catalogue</a>
|
<a href="/" class="btn btn--primary">↶ Return to the catalogue</a>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -96,16 +96,16 @@ const isAdmin = Astro.cookies.get('admin_session')?.value === '1';
|
|||||||
|
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<div class="mt-8 flex flex-wrap items-center gap-3">
|
<div class="mt-8 flex flex-wrap items-center gap-3">
|
||||||
<a href="/admin/editor" class="btn-stamp">
|
<a href="/admin/editor" class="btn btn--primary">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
||||||
New work
|
New work
|
||||||
</a>
|
</a>
|
||||||
<AssetsButton client:idle className="btn-ghost" iconSize={12} />
|
<AssetsButton client:idle className="btn btn--ghost btn--sm" iconSize={12} />
|
||||||
<a href="/admin/messages" class="btn-ghost">
|
<a href="/admin/messages" class="btn btn--ghost">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/></svg>
|
||||||
Messages
|
Messages
|
||||||
</a>
|
</a>
|
||||||
<a href="/admin/settings" class="btn-ghost">
|
<a href="/admin/settings" class="btn btn--ghost">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/></svg>
|
||||||
Settings
|
Settings
|
||||||
</a>
|
</a>
|
||||||
@@ -128,7 +128,7 @@ const isAdmin = Astro.cookies.get('admin_session')?.value === '1';
|
|||||||
</p>
|
</p>
|
||||||
<p class="font-sans text-[var(--subtext1)] mt-4">Check back soon.</p>
|
<p class="font-sans text-[var(--subtext1)] mt-4">Check back soon.</p>
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<a href="/admin/editor" class="btn-stamp mt-8">Create the first post</a>
|
<a href="/admin/editor" class="btn btn--primary mt-8">Create the first post</a>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const displayTitle = post ? (post.title || formatSlug(post.slug)) : 'Work';
|
|||||||
<div class="max-w-2xl mx-auto py-20 md:py-32 text-center">
|
<div class="max-w-2xl mx-auto py-20 md:py-32 text-center">
|
||||||
<div class="font-display italic text-[var(--subtext0)] text-sm tracking-[0.3em] uppercase mb-4">Pardon —</div>
|
<div class="font-display italic text-[var(--subtext0)] text-sm tracking-[0.3em] uppercase mb-4">Pardon —</div>
|
||||||
<h2 class="font-display italic text-3xl md:text-5xl text-[var(--mauve)] mb-6 leading-tight">{error}</h2>
|
<h2 class="font-display italic text-3xl md:text-5xl text-[var(--mauve)] mb-6 leading-tight">{error}</h2>
|
||||||
<a href="/" class="btn-ghost">← Return to the catalogue</a>
|
<a href="/" class="btn btn--ghost">← Return to the catalogue</a>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ const displayTitle = post ? (post.title || formatSlug(post.slug)) : 'Work';
|
|||||||
|
|
||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<a href={`/admin/editor?edit=${encodeURIComponent(post.slug)}`} class="btn-ghost">
|
<a href={`/admin/editor?edit=${encodeURIComponent(post.slug)}`} class="btn btn--ghost">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -914,58 +914,98 @@ code, pre, kbd, samp {
|
|||||||
0 14px 40px -24px rgba(0, 0, 0, 0.9);
|
0 14px 40px -24px rgba(0, 0, 0, 0.9);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ───── Buttons ───── */
|
/* ───── Buttons — one system ─────
|
||||||
.btn-stamp {
|
* 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;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
padding: 0 1.2rem;
|
||||||
font-family: var(--font-display);
|
font-family: var(--font-display);
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 0.95rem;
|
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);
|
background: var(--mauve);
|
||||||
color: var(--rosewater);
|
color: var(--rosewater);
|
||||||
border: 1px solid var(--mauve);
|
border-color: 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;
|
|
||||||
box-shadow: 0 4px 0 -2px color-mix(in srgb, var(--mauve) 60%, black);
|
box-shadow: 0 4px 0 -2px color-mix(in srgb, var(--mauve) 60%, black);
|
||||||
}
|
}
|
||||||
.btn-stamp:hover {
|
.btn--primary:hover {
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
background: var(--red);
|
background: var(--red);
|
||||||
border-color: var(--red);
|
border-color: var(--red);
|
||||||
box-shadow: 0 6px 0 -2px color-mix(in srgb, var(--red) 60%, black);
|
box-shadow: 0 6px 0 -2px color-mix(in srgb, var(--red) 60%, black);
|
||||||
}
|
}
|
||||||
.btn-stamp:active {
|
.btn--primary:active {
|
||||||
transform: translateY(1px);
|
transform: translateY(1px);
|
||||||
box-shadow: 0 1px 0 -1px color-mix(in srgb, var(--mauve) 60%, black);
|
box-shadow: 0 1px 0 -1px color-mix(in srgb, var(--mauve) 60%, black);
|
||||||
}
|
}
|
||||||
.btn-ghost {
|
.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;
|
|
||||||
color: var(--subtext1);
|
color: var(--subtext1);
|
||||||
border: 1px solid var(--surface2);
|
border-color: var(--surface2);
|
||||||
border-radius: 1px;
|
background: color-mix(in srgb, var(--surface0) 45%, transparent);
|
||||||
letter-spacing: 0.06em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
|
||||||
}
|
}
|
||||||
.btn-ghost:hover {
|
.btn--ghost:hover {
|
||||||
color: var(--mauve);
|
color: var(--mauve);
|
||||||
border-color: var(--mauve);
|
border-color: color-mix(in srgb, var(--mauve) 50%, var(--surface2));
|
||||||
background: color-mix(in srgb, var(--mauve) 8%, transparent);
|
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 ───── */
|
/* ───── Top-bar controls — one height, one language ───── */
|
||||||
.topbar-control {
|
.topbar-control {
|
||||||
|
|||||||
Reference in New Issue
Block a user