ui-redesign #1
11 changed files with 129 additions and 118 deletions
2
mise.toml
Normal file
2
mise.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[tools]
|
||||
claude = "latest"
|
||||
|
|
@ -28,72 +28,79 @@ function isActive(href: string): boolean {
|
|||
}
|
||||
---
|
||||
|
||||
|
||||
<header class="sticky top-0 z-40 border-b border-border/80 bg-bg/85 backdrop-blur">
|
||||
<div class="mx-auto flex max-w-4/5 items-center justify-between gap-4 px-4 py-3 sm:px-6">
|
||||
<a href={homeHref} class="flex shrink-0 items-center gap-2.5" aria-label={t('site.name')}>
|
||||
<img src="/favicon.svg" alt="" width="32" height="32" class="size-8" />
|
||||
<div class="flex flex-col justify-center gap-4 px-4 py-3 sm:px-6">
|
||||
<div class="flex flex-row justify-between items-end">
|
||||
<a href={homeHref} class="flex hover:underline shrink-0 items-center gap-2.5" aria-label={t('site.name')}>
|
||||
<img src="/logo.png" class="h-4"/>
|
||||
<span class="font-mono text-[15px] font-semibold tracking-tight text-text">{t('site.name')}</span>
|
||||
</a>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<nav class="hidden items-center gap-1 sm:flex justify-right" aria-label={t('nav.menu')}>
|
||||
{navLinks.map((link) => (
|
||||
<a
|
||||
href={link.href}
|
||||
aria-current={isActive(link.href) ? 'page' : undefined}
|
||||
class:list={[
|
||||
'px-2 py-1 text-sm font-medium font-mono hover:underline',
|
||||
isActive(link.href)
|
||||
? 'bg-primary/10 text-primary'
|
||||
: 'text-text-muted hover:bg-surface-alt hover:text-text',
|
||||
]}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div class="ml-10 flex sm:hidden justify-end gap-3">
|
||||
<a
|
||||
href={langSwitchHref}
|
||||
hreflang={otherLang}
|
||||
title={t('lang.switch')}
|
||||
aria-label={`${t('lang.switch')}: ${LANG_LABELS[otherLang]}`}
|
||||
class="text-accent bg-accent/10 px-2 py-1 font-mono text-xs hover:underline hover:cursor-pointer"
|
||||
class="text-accent max-w-fit font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep"
|
||||
>
|
||||
{`${t('lang.switch')}: ${LANG_LABELS[otherLang]}`}
|
||||
</a>
|
||||
|
||||
<ThemeToggle lang={lang} />
|
||||
</div>
|
||||
<div class="hidden sm:flex items-center gap-2">
|
||||
|
||||
<details class="relative sm:hidden">
|
||||
<summary
|
||||
class="grid size-9 cursor-pointer list-none place-items-center rounded-full border border-border text-text-muted transition-colors hover:border-primary hover:text-primary"
|
||||
aria-label={t('nav.menu')}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" class="size-[18px]">
|
||||
<path d="M4 6h16M4 12h16M4 18h16"></path>
|
||||
</svg>
|
||||
</summary>
|
||||
<nav
|
||||
class="absolute right-0 top-11 flex w-40 flex-col gap-1 rounded-xl border border-border bg-surface p-2 shadow-lg"
|
||||
aria-label={t('nav.menu')}
|
||||
>
|
||||
<nav class="flex items-center gap-1 justify-right" aria-label={t('nav.menu')}>
|
||||
{navLinks.map((link) => (
|
||||
<a
|
||||
href={link.href}
|
||||
aria-current={isActive(link.href) ? 'page' : undefined}
|
||||
class:list={[
|
||||
'rounded-lg px-3 py-2 text-xs font-medium transition-colors',
|
||||
'px-2 py-1 text-sm font-medium font-mono underline',
|
||||
isActive(link.href)
|
||||
? 'bg-primary/10 text-primary'
|
||||
: 'text-text-muted hover:bg-surface-alt hover:text-text',
|
||||
? 'text-primary hover:text-primary-hover'
|
||||
: 'text-text-muted hover:text-text',
|
||||
]}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
</details>
|
||||
<a
|
||||
href={langSwitchHref}
|
||||
hreflang={otherLang}
|
||||
title={t('lang.switch')}
|
||||
aria-label={`${t('lang.switch')}: ${LANG_LABELS[otherLang]}`}
|
||||
class="text-accent max-w-fit py-1 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep "
|
||||
>
|
||||
{`${t('lang.switch')}: ${LANG_LABELS[otherLang]}`}
|
||||
</a>
|
||||
|
||||
<ThemeToggle lang={lang} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end sm:hidden items-center gap-2">
|
||||
<nav class="flex items-center gap-1 justify-right" aria-label={t('nav.menu')}>
|
||||
{navLinks.map((link) => (
|
||||
<a
|
||||
href={link.href}
|
||||
aria-current={isActive(link.href) ? 'page' : undefined}
|
||||
class:list={[
|
||||
'px-2 py-1 text-sm font-medium font-mono underline',
|
||||
isActive(link.href)
|
||||
? 'text-primary hover:text-primary-hover'
|
||||
: 'text-text-muted hover:text-text',
|
||||
]}
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
|
|
|||
|
|
@ -15,47 +15,54 @@ const postsHref = getLocalizedPath('/posts', lang);
|
|||
const aboutHref = getLocalizedPath('/about', lang);
|
||||
---
|
||||
|
||||
<section class="mx-auto max-w-3xl px-4 pb-4 pt-14 sm:px-6 sm:pt-20 font-mono">
|
||||
<div class="flex flex-col items-start gap-8 sm:flex-row sm:items-center">
|
||||
<img
|
||||
src="/logo.png"
|
||||
alt=""
|
||||
width="867"
|
||||
height="458"
|
||||
class="w-40 shrink-0 drop-shadow-sm sm:w-52"
|
||||
/>
|
||||
<div>
|
||||
<p class="text-sm font-semibold uppercase tracking-wide text-primary">{t('hero.kicker')}</p>
|
||||
<h1 class="mt-2 text-3xl font-bold tracking-tight text-text sm:text-4xl">{t('hero.title')}</h1>
|
||||
<p class="mt-4 max-w-xl text-lg leading-relaxed text-text-muted">{t('hero.subtitle')}</p>
|
||||
<div class="mt-6 flex flex-wrap gap-5">
|
||||
<a
|
||||
href={postsHref}
|
||||
class="text-primary hover:underline font-bold px-2 py-1 bg-primary/10"
|
||||
>
|
||||
{t('hero.cta.posts')}
|
||||
</a>
|
||||
<a
|
||||
href={aboutHref}
|
||||
class="text-text hover:underline font-bold px-2 py-1 bg-text/5"
|
||||
>
|
||||
{t('hero.cta.about')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<section class="mx-10 my-10 flex flex-col gap-5 font-mono">
|
||||
<div class="flex flex-col gap-2">
|
||||
<h1 class="text-text text-2xl">{t('hero.title')}</h1>
|
||||
<h2 class="text-text text-lg">{t('hero.subtitle')}</h2>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-3xl px-4 py-12 sm:px-6 font-mono">
|
||||
<!-- <section class="mx-auto max-w-3xl px-4 pb-4 pt-14 sm:px-6 sm:pt-20 font-mono"> -->
|
||||
<!-- <div class="flex flex-col items-start gap-8 sm:flex-row sm:items-center"> -->
|
||||
<!-- <img -->
|
||||
<!-- src="/logo.png" -->
|
||||
<!-- alt="" -->
|
||||
<!-- width="867" -->
|
||||
<!-- height="458" -->
|
||||
<!-- class="w-40 shrink-0 drop-shadow-sm sm:w-52" -->
|
||||
<!-- /> -->
|
||||
<!-- <div> -->
|
||||
<!-- <p class="text-sm font-semibold uppercase tracking-wide text-primary">{t('hero.kicker')}</p> -->
|
||||
<!-- <h1 class="mt-2 text-3xl font-bold tracking-tight text-text sm:text-4xl">{t('hero.title')}</h1> -->
|
||||
<!-- <p class="mt-4 max-w-xl text-lg leading-relaxed text-text-muted">{t('hero.subtitle')}</p> -->
|
||||
<!-- <div class="mt-6 flex flex-wrap gap-5"> -->
|
||||
<!-- <a -->
|
||||
<!-- href={postsHref} -->
|
||||
<!-- class="text-primary hover:underline font-bold px-2 py-1 bg-primary/10" -->
|
||||
<!-- > -->
|
||||
<!-- {t('hero.cta.posts')} -->
|
||||
<!-- </a> -->
|
||||
<!-- <a -->
|
||||
<!-- href={aboutHref} -->
|
||||
<!-- class="text-text hover:underline font-bold px-2 py-1 bg-text/5" -->
|
||||
<!-- > -->
|
||||
<!-- {t('hero.cta.about')} -->
|
||||
<!-- </a> -->
|
||||
<!-- </div> -->
|
||||
<!-- </div> -->
|
||||
<!-- </div> -->
|
||||
<!-- </section> -->
|
||||
|
||||
<section class="mx-10 font-mono">
|
||||
<div class="flex items-baseline justify-between gap-4">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-text">{t('home.recent')}</h2>
|
||||
<p class="mt-1 text-sm text-text-muted">{t('home.recent.subtitle')}</p>
|
||||
</div>
|
||||
<p class="mt-1 text-sm text-text-muted mb-2">{t('home.recent.subtitle')}</p>
|
||||
<a href={postsHref} class="shrink-0 text-sm font-medium text-primary hover:underline">
|
||||
{t('home.viewAll')} →
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{recentPosts.length > 0 ? (
|
||||
<div class="mt-6">
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const tagsHref = (tag: string) => getLocalizedPath(`/tags/${tag}`, lang);
|
|||
<time datetime={post.data.pubDate.toISOString()} class="text-xs font-medium uppercase tracking-wide text-text-muted">
|
||||
{formatDate(post.data.pubDate, lang)}
|
||||
</time>
|
||||
<h3 class="mt-1.5 text-xl font-semibold text-text transition-colors group-hover:text-primary group-hover:underline">
|
||||
<h3 class="mt-1.5 text-xl font-semibold text-text group-hover:text-primary group-hover:underline">
|
||||
{post.data.title}
|
||||
</h3>
|
||||
<p class="mt-1.5 text-[15px] leading-relaxed text-text-muted">{post.data.description}</p>
|
||||
|
|
@ -28,7 +28,7 @@ const tagsHref = (tag: string) => getLocalizedPath(`/tags/${tag}`, lang);
|
|||
{post.data.tags.map((tag) => (
|
||||
<a
|
||||
href={tagsHref(tag)}
|
||||
class="text-text-muted bg-text-muted/10 px-1 py-0.5 text-sm hover:text-accent hover:bg-accent/10"
|
||||
class="text-text-muted px-1 py-0.5 text-sm hover:text-accent underline"
|
||||
>
|
||||
#{tag}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -15,19 +15,19 @@ const posts = tag ? allPosts.filter((p) => p.data.tags.includes(tag)) : allPosts
|
|||
const postsHref = getLocalizedPath('/posts', lang);
|
||||
---
|
||||
|
||||
<section class="mx-auto max-w-3xl px-4 py-12 sm:px-6">
|
||||
<section class="mx-10 mt-10">
|
||||
{tag ? (
|
||||
<>
|
||||
<p class="text-sm font-medium text-text-muted">{t('posts.filteredBy')}</p>
|
||||
<h1 class="mt-1 text-3xl font-bold tracking-tight text-text">#{tag}</h1>
|
||||
<a href={postsHref} class="mt-2 inline-block text-sm font-medium text-primary hover:underline">
|
||||
<p class="text-sm font-mono font-medium text-text-muted">{t('posts.filteredBy')}</p>
|
||||
<h1 class="mt-1 font-mono text-3xl font-bold tracking-tight text-text">#{tag}</h1>
|
||||
<a href={postsHref} class="font-mono mt-2 inline-block text-sm font-medium text-primary hover:underline">
|
||||
{t('posts.clearFilter')}
|
||||
</a>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<h1 class="text-3xl font-bold tracking-tight text-text">{t('posts.title')}</h1>
|
||||
<p class="mt-2 text-text-muted">{t('posts.subtitle')}</p>
|
||||
<h1 class="font-mono text-3xl font-bold tracking-tight text-text">{t('posts.title')}</h1>
|
||||
<p class="font-mono mt-2 text-text-muted">{t('posts.subtitle')}</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,31 +11,26 @@ const t = useTranslations(lang);
|
|||
---
|
||||
|
||||
<button
|
||||
id="theme-toggle"
|
||||
type="button"
|
||||
class="theme-toggle text-accent sm:ml-2 font-mono text-xs sm:text-sm underline hover:cursor-pointer hover:text-accent-deep"
|
||||
aria-label={t('theme.switch')}
|
||||
title={t('theme.switch')}
|
||||
class="text-accent bg-accent/10 px-2 py-1 font-mono text-xs hover:underline hover:cursor-pointer"
|
||||
>
|
||||
{t('theme.switch')}
|
||||
</button>
|
||||
|
||||
<script is:inline>
|
||||
(function () {
|
||||
const btn = document.getElementById('theme-toggle');
|
||||
|
||||
function sync() {
|
||||
const isDark = document.documentElement.classList.contains('dark');
|
||||
sun.classList.toggle('hidden', !isDark);
|
||||
moon.classList.toggle('hidden', isDark);
|
||||
}
|
||||
|
||||
<script>
|
||||
function init() {
|
||||
const buttons = document.querySelectorAll('.theme-toggle');
|
||||
buttons.forEach((btn) => {
|
||||
btn.addEventListener('click', function () {
|
||||
const isDark = document.documentElement.classList.toggle('dark');
|
||||
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
||||
sync();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
sync();
|
||||
})();
|
||||
init();
|
||||
|
||||
document.addEventListener('astro:after-swap', init);
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ const postsHref = getLocalizedPath('/posts', lang);
|
|||
const tagsHref = (tag: string) => getLocalizedPath(`/tags/${tag}`, lang);
|
||||
---
|
||||
|
||||
<article class="mx-auto max-w-3xl px-4 py-12 sm:px-6">
|
||||
<a href={postsHref} class="inline-flex items-center gap-1.5 text-sm font-medium text-text-muted transition-colors hover:text-primary">
|
||||
<article class="mx-10 mt-10 font-mono">
|
||||
<a href={postsHref} class="underline inline-flex items-center gap-1.5 text-sm font-medium text-text-muted hover:text-primary">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">
|
||||
<path d="M15 18l-6-6 6-6"></path>
|
||||
</svg>
|
||||
|
|
@ -42,7 +42,7 @@ const tagsHref = (tag: string) => getLocalizedPath(`/tags/${tag}`, lang);
|
|||
{post.data.tags.map((tag) => (
|
||||
<a
|
||||
href={tagsHref(tag)}
|
||||
class="rounded-full bg-surface-alt px-2.5 py-1 text-xs font-medium text-text-muted transition-colors hover:bg-accent/15 hover:text-accent"
|
||||
class=" text-xs font-medium text-text-muted underline hover:text-accent"
|
||||
>
|
||||
#{tag}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export const ui = {
|
|||
'hero.kicker': "Gabriel Franco's notes",
|
||||
'hero.title': 'Building things, and figuring out how they work.',
|
||||
'hero.subtitle':
|
||||
"Notes on computer science, systems, and the open source projects that shaped how I think about software. I'm gabeefranco — welcome.",
|
||||
"Notes on computer science, systems, and the open source projects that shaped how I think about software. I'm Gabriel Franco — welcome.",
|
||||
'hero.cta.posts': 'Read the posts',
|
||||
'hero.cta.about': 'About me',
|
||||
'home.recent': 'Recent posts',
|
||||
|
|
@ -54,7 +54,7 @@ export const ui = {
|
|||
'hero.kicker': 'Anotações do Gabriel Franco',
|
||||
'hero.title': 'Construindo coisas e entendendo como elas funcionam.',
|
||||
'hero.subtitle':
|
||||
'Notas sobre ciência da computação, sistemas e os projetos open source que moldaram a forma como penso sobre software. Eu sou o gabeefranco — seja bem-vindo.',
|
||||
'Notas sobre ciência da computação, sistemas e os projetos open source que moldaram a forma como penso sobre software. Eu sou o Gabriel Franco — seja bem-vindo.',
|
||||
'hero.cta.posts': 'Ler os posts',
|
||||
'hero.cta.about': 'Sobre mim',
|
||||
'home.recent': 'Posts recentes',
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const langs: Lang[] = ['en', 'pt'];
|
|||
---
|
||||
|
||||
<BaseLayout title={`${ui.en['404.title']} — gabeefranco`} description={ui.en['404.body']} lang="en">
|
||||
<section class="mx-auto flex max-w-3xl flex-col items-start px-4 py-24 sm:px-6">
|
||||
<section class="mx-10 flex flex-col items-start px-4 py-24 sm:px-6 font-mono">
|
||||
<p class="font-mono text-sm text-primary">404</p>
|
||||
{langs.map((lang) => (
|
||||
<div data-notfound-lang={lang} class={lang === 'en' ? '' : 'hidden'}>
|
||||
|
|
@ -18,7 +18,7 @@ const langs: Lang[] = ['en', 'pt'];
|
|||
<p class="mt-3 text-text-muted">{ui[lang]['404.body']}</p>
|
||||
<a
|
||||
href={getLocalizedPath('/', lang)}
|
||||
class="mt-6 inline-block rounded-full bg-primary px-5 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-primary-hover"
|
||||
class="mt-4 inline-block underline text-primary hover:text-primary-hover font-bold"
|
||||
>
|
||||
{ui[lang]['404.cta']}
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const description =
|
|||
---
|
||||
|
||||
<BaseLayout title={`${t('about.title')} — ${t('site.name')}`} description={description} lang="en">
|
||||
<section class="mx-auto max-w-3xl px-4 py-14 sm:px-6">
|
||||
<section class="mx-10 mt-10 font-mono">
|
||||
<h1 class="text-3xl font-bold tracking-tight text-text sm:text-4xl">{t('about.title')}</h1>
|
||||
|
||||
<div class="prose prose-neutral mt-6 max-w-none prose-headings:font-semibold prose-a:text-primary prose-a:no-underline hover:prose-a:underline dark:prose-invert">
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ const description =
|
|||
---
|
||||
|
||||
<BaseLayout title={`${t('about.title')} — ${t('site.name')}`} description={description} lang="pt">
|
||||
<section class="mx-auto max-w-3xl px-4 py-14 sm:px-6">
|
||||
<section class="mx-10 mt-10 font-mono">
|
||||
<h1 class="text-3xl font-bold tracking-tight text-text sm:text-4xl">{t('about.title')}</h1>
|
||||
|
||||
<div class="prose prose-neutral mt-6 max-w-none prose-headings:font-semibold prose-a:text-primary prose-a:no-underline hover:prose-a:underline dark:prose-invert">
|
||||
|
|
|
|||
Loading…
Reference in a new issue