feat: implement design system — domain colors, date typography, interaction states, CV family colors
Apply the collaboratively-written design system from .interface-design/system.md: - Domain color map: Social pages (rose), Code pages (emerald), Reading pages (orange), Music pages (purple) replace generic accent tokens - Font-mono on all <time> elements via global CSS rule + wrap 7 non-<time> dates - Shadow standardization: replace hover:shadow-lg with border-hover on news cards - Active states: scale(0.97) press feedback on buttons and pagination links - Gradient cleanup: remove to-white gradient on github featured project cards - CV Skills & Interests: cycle through 8 distinct colors per family category (amber, emerald, sky, rose, purple, orange, teal, indigo) on both CV page and homepage - Update system.md with refined palette documentation and domain color spec Files: 19 changed across templates, CSS, and design documentation Confab-Link: http://localhost:8080/sessions/bd3f7012-c703-47e9-bfe2-2ad04ce1842d
This commit is contained in:
@@ -1,93 +1,152 @@
|
||||
# Design System — rmendes.net
|
||||
|
||||
## Direction: Workshop Terminal
|
||||
## Palette
|
||||
|
||||
**Feel:** A well-configured terminal in a warm room. Structure is precise and technical. Warmth comes from surfaces and content, not decorative color. Color is signal, not decoration.
|
||||
**Surfaces:** Warm stone — not yellow, not cold. The difference is felt, not seen.
|
||||
|
||||
**Signature:** Chromatically quiet at rest, vivid in context. The base is warm monochrome. Color enters when content demands it — music pages glow purple, code pages pulse emerald, social feeds carry brand colors. You feel the shift as you navigate between worlds.
|
||||
| Token | Hex | Role |
|
||||
|-------|-----|------|
|
||||
| surface-50 | `#faf8f5` | Canvas (light) |
|
||||
| surface-100 | `#f4f2ee` | Cards, elevated surfaces (light) |
|
||||
| surface-200 | `#e8e5df` | Standard borders, dividers |
|
||||
| surface-300 | `#d5d0c8` | Strong borders, input borders |
|
||||
| surface-400 | `#a09a90` | Muted text, placeholders |
|
||||
| surface-500 | `#7a746a` | Secondary text |
|
||||
| surface-600 | `#5c5750` | Supporting text |
|
||||
| surface-700 | `#3f3b35` | Dark mode borders |
|
||||
| surface-800 | `#2a2722` | Cards, elevated surfaces (dark) |
|
||||
| surface-900 | `#1c1b19` | Canvas (dark) |
|
||||
| surface-950 | `#0f0e0d` | Deepest dark |
|
||||
|
||||
## Intent
|
||||
**Accent (Warm Amber):** Default interactive color — links, CTAs, focus rings.
|
||||
|
||||
**Who:** Visitors to a DevOps engineer's personal site — peers, recruiters, fellow IndieWeb enthusiasts, RSS subscribers. They're reading, browsing, discovering.
|
||||
|
||||
**What:** Read posts, explore interests, follow feeds, understand who this person is.
|
||||
|
||||
**How it should feel:** Like opening a well-used notebook in a workshop. Technical precision meets personal warmth. Not corporate, not cold, not decorative.
|
||||
|
||||
## Surfaces — Warm Stone
|
||||
|
||||
Warm neutral tones. Not yellow — just not cold. The difference is felt, not seen.
|
||||
|
||||
| Token | Light | Dark | Role |
|
||||
|-------|-------|------|------|
|
||||
| 50 | `#faf8f5` | — | Canvas background |
|
||||
| 100 | `#f4f2ee` | — | Card surfaces |
|
||||
| 200 | `#e8e5df` | — | Borders, dividers |
|
||||
| 300 | `#d5d0c8` | — | Strong borders |
|
||||
| 400 | `#a09a90` | — | Muted/placeholder text |
|
||||
| 500 | `#7a746a` | — | Secondary text |
|
||||
| 600 | `#5c5750` | — | — |
|
||||
| 700 | `#3f3b35` | — | Dark text secondary |
|
||||
| 800 | `#2a2722` | — | Card surfaces (dark) |
|
||||
| 900 | `#1c1b19` | — | Canvas (dark) |
|
||||
| 950 | `#0f0e0d` | — | Deepest dark |
|
||||
|
||||
## Text Hierarchy
|
||||
|
||||
| Level | Light | Dark | Usage |
|
||||
|-------|-------|------|-------|
|
||||
| Primary | surface-900 `#1c1b19` | surface-50 `#faf8f5` | Headlines, body |
|
||||
| Secondary | surface-600 `#5c5750` | surface-400 `#a09a90` | Supporting text |
|
||||
| Muted | surface-400 `#a09a90` | surface-600 `#5c5750` | Metadata, timestamps |
|
||||
| Faint | surface-300 `#d5d0c8` | surface-700 `#3f3b35` | Disabled, decorative |
|
||||
|
||||
## Interactive — Accent (Warm Amber)
|
||||
|
||||
Default interactive color for generic links, CTAs, focus rings. Terminal amber warmth.
|
||||
|
||||
| Weight | Value | Usage |
|
||||
|--------|-------|-------|
|
||||
| 400 | `#fbbf24` | Dark mode links, hover states |
|
||||
| 500 | `#f59e0b` | — |
|
||||
| 600 | `#d97706` | Light mode links, buttons |
|
||||
| 700 | `#b45309` | Light mode hover |
|
||||
| Token | Hex | Usage |
|
||||
|-------|-----|-------|
|
||||
| accent-400 | `#fbbf24` | Dark mode: links, hover |
|
||||
| accent-500 | `#f59e0b` | — |
|
||||
| accent-600 | `#d97706` | Light mode: links, buttons |
|
||||
| accent-700 | `#b45309` | Light mode: hover, pressed |
|
||||
|
||||
## Domain Colors
|
||||
|
||||
Each section of the site owns its chromatic identity. These override accent on their respective pages.
|
||||
Every section of the site has a color identity. On domain pages, links, hover states, and card borders use the domain color instead of amber.
|
||||
|
||||
| Domain | Tailwind Color | Pages |
|
||||
|--------|---------------|-------|
|
||||
| Writing/Blog | amber (= accent) | blog, articles, notes, homepage |
|
||||
| Code/GitHub | emerald | github, repo widgets |
|
||||
| Music | violet | funkwhale, listening, last.fm |
|
||||
| Bluesky | `#0085ff` | social-activity bluesky tab |
|
||||
| Mastodon | `#a730b8` | social-activity mastodon tab |
|
||||
| Bookmarks | amber | bookmarks page |
|
||||
| Likes | rose | likes page |
|
||||
| Replies | sky | replies page |
|
||||
| Reposts | emerald | reposts page |
|
||||
| Photos | violet | photos page |
|
||||
| RSS/Podcasts | orange | podroll, subscribe, RSS links |
|
||||
| CV | neutral (no accent) | cv page |
|
||||
### Domain Map (complete — every page accounted for)
|
||||
|
||||
| Domain | Tailwind color | Light text | Dark text | Pages |
|
||||
|--------|---------------|------------|-----------|-------|
|
||||
| **Writing** | amber (= accent) | amber-700 | amber-400 | `/blog/`, `/articles/`, `/notes/`, `/bookmarks/`, `/digest/`, `/news/`, `/categories/`, individual posts |
|
||||
| **Social** | rose | rose-600 | rose-400 | `/likes/`, `/replies/`, `/reposts/`, `/interactions/` |
|
||||
| **Code** | emerald | emerald-600 | emerald-400 | `/github/`, `/github/starred/` |
|
||||
| **Music** | purple | purple-600 | purple-400 | `/funkwhale/`, `/listening/` |
|
||||
| **Video** | red | red-600 | red-400 | `/youtube/` |
|
||||
| **Reading** | orange | orange-600 | orange-400 | `/blogroll/`, `/podroll/`, `/readlater/` |
|
||||
| **Neutral** | — (use accent) | — | — | `/` (home), `/about/`, `/cv/`, `/slashes/`, `/search/`, `/changelog/`, `/404` |
|
||||
|
||||
### Brand Colors (hardcoded hex — not domain colors)
|
||||
|
||||
These are third-party brand colors used in syndication badges and social widgets. Not part of the domain system.
|
||||
|
||||
| Brand | Hex | Where |
|
||||
|-------|-----|-------|
|
||||
| Mastodon | `#a730b8` | Syndication badges, social-activity widget |
|
||||
| Bluesky | `#0085ff` | Syndication badges, social-activity widget |
|
||||
| LinkedIn | `#0a66c2` | Syndication badges |
|
||||
| IndieNews | `#ff5c00` | Syndication badges |
|
||||
| Mastodon alt | `#6364ff` | Syndication badges |
|
||||
|
||||
### Domain Prominence (medium)
|
||||
|
||||
On a domain page, these elements adopt the domain color:
|
||||
- **Page title** — tinted text or accent underline
|
||||
- **Links** — `text-[domain]-600 dark:text-[domain]-400`
|
||||
- **Hover states** — `hover:text-[domain]-700 dark:hover:text-[domain]-300`
|
||||
- **Card borders on hover** — `hover:border-[domain]-400 dark:hover:border-[domain]-600`
|
||||
- **Post-type badges** — domain-colored background/text
|
||||
|
||||
These stay neutral (accent or surface) regardless of domain:
|
||||
- Header/navigation
|
||||
- Sidebar widget containers
|
||||
- Footer
|
||||
- Global UI (search, theme toggle)
|
||||
|
||||
## Depth
|
||||
|
||||
**Subtle shadows.** One consistent shadow level for elevated surfaces. Borders + shadow together.
|
||||
|
||||
| Element | Treatment |
|
||||
|---------|-----------|
|
||||
| Cards, widgets | `shadow-sm` + `border border-surface-200 dark:border-surface-700` |
|
||||
| Avatars, album art | `shadow-lg` (depth gives images presence against flat surfaces) |
|
||||
| Modals | `shadow-xl` (overlay needs clear elevation) |
|
||||
| Hover on cards | No shadow change — border color shift only |
|
||||
|
||||
**Gradients are allowed** for:
|
||||
- Now-playing cards (domain color tinted: `bg-gradient-to-br from-[color]-500/10`)
|
||||
- YouTube hero/live cards
|
||||
- Icon containers in reading pages (`from-[color]-400 to-[color]-600`)
|
||||
|
||||
Gradients are NOT used for:
|
||||
- Standard cards, widgets, or page backgrounds
|
||||
|
||||
## Typography
|
||||
|
||||
- **Body:** Inter — clean, technical sans-serif
|
||||
- **Metadata:** `font-mono` for dates, timestamps, stats, version numbers
|
||||
- **Headlines:** Inter bold, tight tracking
|
||||
| Role | Style | Usage |
|
||||
|------|-------|-------|
|
||||
| Headlines | Inter, `font-bold` | Page titles, section headings |
|
||||
| Body | Inter, normal weight | Paragraphs, descriptions |
|
||||
| Labels | Inter, `font-medium` or `font-semibold` | Badges, nav items, metadata labels |
|
||||
| **Dates/timestamps** | **`font-mono text-sm`** | Every `<time>` element, stat numbers, version numbers |
|
||||
| Code | `font-mono` | Commit SHAs, code blocks, technical identifiers |
|
||||
|
||||
## Depth Strategy
|
||||
### Date treatment rule
|
||||
|
||||
**Borders only.** No drop shadows. Low-opacity warm borders define surfaces. Hierarchy comes from surface color shifts, not elevation effects.
|
||||
Every rendered date (via `dateDisplay` or `date()` filter) gets `font-mono`. This adds technical texture throughout the site — like timestamps in a log.
|
||||
|
||||
## Spacing
|
||||
|
||||
Base: 4px (Tailwind default). No custom scale needed.
|
||||
Base: 4px (Tailwind default rem scale).
|
||||
|
||||
Extracted dominant patterns:
|
||||
- Component internal: `p-4` (cards), `p-3` (compact), `p-5` (featured)
|
||||
- Gaps: `gap-2` (tight lists), `gap-3` (standard), `gap-4` (spacious)
|
||||
- Section separation: `mb-6` to `mb-8`
|
||||
- Micro: `px-2 py-0.5` (badges), `px-3 py-1.5` (pills)
|
||||
|
||||
## Border Radius
|
||||
|
||||
| Element | Radius |
|
||||
|---------|--------|
|
||||
| Cards, inputs, buttons | `rounded-lg` (dominant: 124×) |
|
||||
| Avatars, status dots, badges | `rounded-full` (89×) |
|
||||
| Featured/hero cards | `rounded-xl` (21×) |
|
||||
| Now-playing sections | `rounded-xl sm:rounded-2xl` |
|
||||
|
||||
## Interaction States
|
||||
|
||||
Every interactive element needs:
|
||||
- **hover:** color shift (`transition-colors` — already dominant at 93×)
|
||||
- **focus:** visible ring (`focus:ring-2 focus:ring-accent-500` or domain equivalent)
|
||||
- **active:** not currently implemented — add where it matters (buttons)
|
||||
|
||||
Card hover pattern: border color shifts to domain color, no shadow change.
|
||||
|
||||
## Dark Mode
|
||||
|
||||
- Surfaces invert (dark warm canvas, lighter warm cards)
|
||||
- Domain colors use their 400-weight for dark mode (lighter/brighter)
|
||||
- Borders use warm low-opacity rgba
|
||||
- No shadows in either mode
|
||||
- Class-based: `darkMode: "class"` — toggled via button in header
|
||||
- Surfaces invert: light canvas (`surface-50`) → dark canvas (`surface-900`)
|
||||
- Cards: `surface-100` → `surface-800`
|
||||
- Domain colors shift to 400-weight (brighter) in dark mode
|
||||
- Borders: `surface-200` → `surface-700`
|
||||
- Shadows remain `shadow-sm` (less visible but still present for subtle lift)
|
||||
|
||||
## What Needs Implementation
|
||||
|
||||
Audit findings — these are the gaps between this system and the current code:
|
||||
|
||||
1. **font-mono on dates** — 80+ date elements need `font-mono text-sm` added
|
||||
2. **Domain colors on section pages** — page titles, links, hovers, card borders need domain color on their respective pages
|
||||
3. **Shadow standardization** — currently mixed; standardize to the levels defined above
|
||||
4. **Gradient cleanup** — remove `to-white` (github.njk), standardize gradient pattern
|
||||
5. **Focus states** — add `focus:ring-2` to all interactive elements (currently only 10 across 6 files)
|
||||
6. **Active states** — add to buttons
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
{# Last Updated #}
|
||||
{% if cv.lastUpdated %}
|
||||
<p class="text-sm text-surface-500 text-center mt-8">
|
||||
Last updated: {{ cv.lastUpdated | date("PPP") }}
|
||||
Last updated: <time datetime="{{ cv.lastUpdated }}">{{ cv.lastUpdated | date("PPP") }}</time>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{#
|
||||
CV Interests Section - interests grouped by category
|
||||
Data fetched from /cv/data.json via homepage plugin
|
||||
Each family gets a distinct color via cycling palette
|
||||
#}
|
||||
|
||||
{% if cv and cv.interests and (cv.interests | dictsort | length) %}
|
||||
@@ -12,13 +13,31 @@
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{% for category, items in cv.interests %}
|
||||
{% if not filterType or (cv.interestTypes and cv.interestTypes[category] == filterType) or not cv.interestTypes or not cv.interestTypes[category] %}
|
||||
{# Cycle through 8 distinct colors per family using loop.index0 #}
|
||||
{% set ci = loop.index0 % 8 %}
|
||||
<div class="p-4 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700">
|
||||
<h3 class="font-semibold text-sm uppercase tracking-wide text-surface-600 dark:text-surface-400 mb-2">
|
||||
{{ category }}
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
{% for interest in items %}
|
||||
<span class="text-xs px-2 py-1 bg-accent-50 dark:bg-accent-900/30 text-accent-700 dark:text-accent-300 rounded-full">
|
||||
{% if ci == 0 %}
|
||||
<span class="text-xs px-2 py-1 bg-amber-50 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300 rounded-full">
|
||||
{% elif ci == 1 %}
|
||||
<span class="text-xs px-2 py-1 bg-emerald-50 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300 rounded-full">
|
||||
{% elif ci == 2 %}
|
||||
<span class="text-xs px-2 py-1 bg-sky-50 dark:bg-sky-900/30 text-sky-700 dark:text-sky-300 rounded-full">
|
||||
{% elif ci == 3 %}
|
||||
<span class="text-xs px-2 py-1 bg-rose-50 dark:bg-rose-900/30 text-rose-700 dark:text-rose-300 rounded-full">
|
||||
{% elif ci == 4 %}
|
||||
<span class="text-xs px-2 py-1 bg-purple-50 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 rounded-full">
|
||||
{% elif ci == 5 %}
|
||||
<span class="text-xs px-2 py-1 bg-orange-50 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300 rounded-full">
|
||||
{% elif ci == 6 %}
|
||||
<span class="text-xs px-2 py-1 bg-teal-50 dark:bg-teal-900/30 text-teal-700 dark:text-teal-300 rounded-full">
|
||||
{% elif ci == 7 %}
|
||||
<span class="text-xs px-2 py-1 bg-indigo-50 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 rounded-full">
|
||||
{% endif %}
|
||||
{{ interest }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{#
|
||||
CV Skills Section - skills grouped by category
|
||||
Data fetched from /cv/data.json via homepage plugin
|
||||
Each family gets a distinct color via cycling palette
|
||||
#}
|
||||
|
||||
{% if cv and cv.skills and (cv.skills | dictsort | length) %}
|
||||
@@ -12,13 +13,31 @@
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
{% for category, items in cv.skills %}
|
||||
{% if not filterType or (cv.skillTypes and cv.skillTypes[category] == filterType) or not cv.skillTypes or not cv.skillTypes[category] %}
|
||||
{# Cycle through 8 distinct colors per family using loop.index0 #}
|
||||
{% set ci = loop.index0 % 8 %}
|
||||
<div class="p-4 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700">
|
||||
<h3 class="font-semibold text-sm uppercase tracking-wide text-surface-600 dark:text-surface-400 mb-2">
|
||||
{{ category }}
|
||||
</h3>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
{% for skill in items %}
|
||||
<span class="text-xs px-2 py-1 bg-accent-50 dark:bg-accent-900/30 text-accent-700 dark:text-accent-300 rounded-full">
|
||||
{% if ci == 0 %}
|
||||
<span class="text-xs px-2 py-1 bg-amber-50 dark:bg-amber-900/30 text-amber-700 dark:text-amber-300 rounded-full">
|
||||
{% elif ci == 1 %}
|
||||
<span class="text-xs px-2 py-1 bg-emerald-50 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300 rounded-full">
|
||||
{% elif ci == 2 %}
|
||||
<span class="text-xs px-2 py-1 bg-sky-50 dark:bg-sky-900/30 text-sky-700 dark:text-sky-300 rounded-full">
|
||||
{% elif ci == 3 %}
|
||||
<span class="text-xs px-2 py-1 bg-rose-50 dark:bg-rose-900/30 text-rose-700 dark:text-rose-300 rounded-full">
|
||||
{% elif ci == 4 %}
|
||||
<span class="text-xs px-2 py-1 bg-purple-50 dark:bg-purple-900/30 text-purple-700 dark:text-purple-300 rounded-full">
|
||||
{% elif ci == 5 %}
|
||||
<span class="text-xs px-2 py-1 bg-orange-50 dark:bg-orange-900/30 text-orange-700 dark:text-orange-300 rounded-full">
|
||||
{% elif ci == 6 %}
|
||||
<span class="text-xs px-2 py-1 bg-teal-50 dark:bg-teal-900/30 text-teal-700 dark:text-teal-300 rounded-full">
|
||||
{% elif ci == 7 %}
|
||||
<span class="text-xs px-2 py-1 bg-indigo-50 dark:bg-indigo-900/30 text-indigo-700 dark:text-indigo-300 rounded-full">
|
||||
{% endif %}
|
||||
{{ skill }}
|
||||
</span>
|
||||
{% endfor %}
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
class="text-accent-600 dark:text-accent-400 hover:underline"
|
||||
target="_blank"
|
||||
rel="noopener">
|
||||
{{ mention.author.name }} mentioned this on {{ mention.published | date("MMM d, yyyy") }}
|
||||
{{ mention.author.name }} mentioned this on <time datetime="{{ mention.published }}">{{ mention.published | date("MMM d, yyyy") }}</time>
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
||||
34
blogroll.njk
34
blogroll.njk
@@ -6,7 +6,7 @@ permalink: /blogroll/
|
||||
<div class="blogroll-page" x-data="blogrollApp()" x-init="init()">
|
||||
<header class="mb-6 sm:mb-8">
|
||||
<h1 class="text-2xl sm:text-3xl md:text-4xl font-bold text-surface-900 dark:text-surface-100 mb-2">
|
||||
<svg class="w-8 h-8 inline-block mr-2 text-accent-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-8 h-8 inline-block mr-2 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
|
||||
</svg>
|
||||
Blogroll
|
||||
@@ -24,7 +24,7 @@ permalink: /blogroll/
|
||||
<nav class="flex gap-1 overflow-x-auto -mb-px" aria-label="Tabs">
|
||||
<button
|
||||
@click="switchTab('blogs')"
|
||||
:class="activeTab === 'blogs' ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
|
||||
:class="activeTab === 'blogs' ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
|
||||
class="pb-3 px-3 border-b-2 font-medium text-sm transition-colors whitespace-nowrap flex-shrink-0"
|
||||
>
|
||||
All Blogs
|
||||
@@ -32,7 +32,7 @@ permalink: /blogroll/
|
||||
<template x-for="cat in categories" :key="cat.name">
|
||||
<button
|
||||
@click="switchTab('category:' + cat.name)"
|
||||
:class="activeTab === 'category:' + cat.name ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
|
||||
:class="activeTab === 'category:' + cat.name ? 'border-orange-500 text-orange-600 dark:text-orange-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
|
||||
class="pb-3 px-3 border-b-2 font-medium text-sm transition-colors whitespace-nowrap flex-shrink-0"
|
||||
>
|
||||
<span x-text="cat.name"></span>
|
||||
@@ -47,7 +47,7 @@ permalink: /blogroll/
|
||||
<div class="main-content">
|
||||
{# Loading State #}
|
||||
<div x-show="loading" class="text-center py-12">
|
||||
<svg class="w-8 h-8 mx-auto text-accent-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24">
|
||||
<svg class="w-8 h-8 mx-auto text-orange-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
@@ -65,17 +65,17 @@ permalink: /blogroll/
|
||||
<template x-for="blog in blogs" :key="blog.id">
|
||||
<a
|
||||
:href="blog.siteUrl || blog.feedUrl"
|
||||
class="block bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-4 hover:border-accent-400 dark:hover:border-accent-600 transition-colors group"
|
||||
class="block bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-4 hover:border-orange-400 dark:hover:border-orange-600 transition-colors group"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-accent-400 to-accent-600 flex items-center justify-center flex-shrink-0 overflow-hidden">
|
||||
<div class="w-10 h-10 rounded-lg bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center flex-shrink-0 overflow-hidden">
|
||||
<img x-show="blog.photo" :src="blog.photo" class="w-10 h-10 object-cover" loading="lazy" />
|
||||
<span x-show="!blog.photo" class="text-white text-sm font-bold" x-text="blog.title?.charAt(0)?.toUpperCase()"></span>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-medium text-surface-900 dark:text-surface-100 truncate group-hover:text-accent-600 dark:group-hover:text-accent-400 transition-colors" x-text="blog.title"></h3>
|
||||
<h3 class="font-medium text-surface-900 dark:text-surface-100 truncate group-hover:text-orange-600 dark:group-hover:text-orange-400 transition-colors" x-text="blog.title"></h3>
|
||||
<p x-show="blog.category" class="text-xs text-surface-500 truncate" x-text="blog.category"></p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -109,16 +109,16 @@ permalink: /blogroll/
|
||||
{# Category Items Tab (one for each category) #}
|
||||
<div x-show="activeTab.startsWith('category:') && !loading" class="space-y-4">
|
||||
<template x-for="item in categoryItems" :key="item.id">
|
||||
<article class="bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-4 sm:p-6 hover:border-accent-400 dark:hover:border-accent-600 transition-colors">
|
||||
<article class="bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-4 sm:p-6 hover:border-orange-400 dark:hover:border-orange-600 transition-colors">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<h2 class="font-semibold text-lg text-surface-900 dark:text-surface-100">
|
||||
<a :href="item.url" class="hover:text-accent-600 dark:hover:text-accent-400" target="_blank" rel="noopener" x-text="item.title"></a>
|
||||
<a :href="item.url" class="hover:text-orange-600 dark:hover:text-orange-400" target="_blank" rel="noopener" x-text="item.title"></a>
|
||||
</h2>
|
||||
<span
|
||||
x-show="item.isFuture"
|
||||
class="inline-flex items-center gap-1 px-2 py-0.5 bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400 rounded-full text-xs font-medium flex-shrink-0"
|
||||
class="inline-flex items-center gap-1 px-2 py-0.5 bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400 rounded-full text-xs font-medium flex-shrink-0"
|
||||
>
|
||||
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
@@ -129,7 +129,7 @@ permalink: /blogroll/
|
||||
<div class="flex flex-wrap items-center gap-2 text-sm text-surface-500 mb-3">
|
||||
<a
|
||||
:href="item.blog?.siteUrl || '#'"
|
||||
class="inline-flex items-center gap-1 px-2 py-0.5 bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400 rounded-full hover:bg-amber-200 dark:hover:bg-amber-900/50 transition-colors"
|
||||
class="inline-flex items-center gap-1 px-2 py-0.5 bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400 rounded-full hover:bg-orange-200 dark:hover:bg-orange-900/50 transition-colors"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
@@ -155,7 +155,7 @@ permalink: /blogroll/
|
||||
<div class="flex flex-wrap items-center gap-3 mt-4 pt-4 border-t border-surface-200 dark:border-surface-700">
|
||||
<a
|
||||
:href="item.url"
|
||||
class="inline-flex items-center gap-2 text-sm text-accent-600 hover:text-accent-700 dark:text-accent-400"
|
||||
class="inline-flex items-center gap-2 text-sm text-orange-600 hover:text-orange-700 dark:text-orange-400"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
@@ -206,7 +206,7 @@ permalink: /blogroll/
|
||||
<button
|
||||
@click="loadMoreCategory()"
|
||||
:disabled="loadingMore"
|
||||
class="px-6 py-3 bg-accent-600 hover:bg-accent-700 text-white rounded-lg text-sm font-medium transition-colors disabled:opacity-50"
|
||||
class="px-6 py-3 bg-orange-600 hover:bg-orange-700 text-white rounded-lg text-sm font-medium transition-colors disabled:opacity-50"
|
||||
>
|
||||
<span x-show="!loadingMore">Load More</span>
|
||||
<span x-show="loadingMore" class="flex items-center gap-2">
|
||||
@@ -255,11 +255,11 @@ permalink: /blogroll/
|
||||
<h3 class="widget-title">Stats</h3>
|
||||
<div class="grid grid-cols-2 gap-3 text-center">
|
||||
<div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg">
|
||||
<span class="text-2xl font-bold text-accent-600 dark:text-accent-400 block" x-text="status?.blogs?.count || 0"></span>
|
||||
<span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="status?.blogs?.count || 0"></span>
|
||||
<span class="text-xs text-surface-500 uppercase">Blogs</span>
|
||||
</div>
|
||||
<div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg">
|
||||
<span class="text-2xl font-bold text-accent-600 dark:text-accent-400 block" x-text="status?.items?.count || 0"></span>
|
||||
<span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="status?.items?.count || 0"></span>
|
||||
<span class="text-xs text-surface-500 uppercase">Posts</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -268,7 +268,7 @@ permalink: /blogroll/
|
||||
{# Category Quick Links (for when on blogs tab) #}
|
||||
<div class="widget" x-show="categories.length > 0">
|
||||
<h3 class="widget-title flex items-center gap-2">
|
||||
<svg class="w-5 h-5 text-accent-600" fill="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-5 h-5 text-orange-600" fill="currentColor" viewBox="0 0 24 24">
|
||||
<circle cx="6.18" cy="17.82" r="2.18"/>
|
||||
<path d="M4 4.44v2.83c7.03 0 12.73 5.7 12.73 12.73h2.83c0-8.59-6.97-15.56-15.56-15.56zm0 5.66v2.83c3.9 0 7.07 3.17 7.07 7.07h2.83c0-5.47-4.43-9.9-9.9-9.9z"/>
|
||||
</svg>
|
||||
@@ -279,7 +279,7 @@ permalink: /blogroll/
|
||||
<li>
|
||||
<button
|
||||
@click="switchTab('category:' + cat.name)"
|
||||
:class="activeTab === 'category:' + cat.name ? 'bg-amber-100 dark:bg-amber-900/30 text-amber-700 dark:text-amber-400' : 'hover:bg-surface-100 dark:hover:bg-surface-700'"
|
||||
:class="activeTab === 'category:' + cat.name ? 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400' : 'hover:bg-surface-100 dark:hover:bg-surface-700'"
|
||||
class="w-full text-left px-3 py-2 rounded-lg text-sm transition-colors"
|
||||
>
|
||||
<span x-text="cat.name"></span>
|
||||
|
||||
@@ -466,6 +466,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Active states — subtle press feedback on buttons */
|
||||
@layer base {
|
||||
button:active:not(:disabled),
|
||||
.pagination-link:active:not(.disabled) {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
}
|
||||
|
||||
/* Video embeds */
|
||||
@layer components {
|
||||
.video-embed {
|
||||
@@ -520,6 +528,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* Dates — monospace for technical texture (system.md: every <time> gets font-mono) */
|
||||
@layer base {
|
||||
time {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply content-visibility to images and post items for performance */
|
||||
@layer base {
|
||||
/* Responsive typography */
|
||||
|
||||
2
cv.njk
2
cv.njk
@@ -127,7 +127,7 @@ pagefindIgnore: true
|
||||
{# Last Updated #}
|
||||
{% if cv.lastUpdated %}
|
||||
<p class="text-sm text-surface-500 text-center mt-8">
|
||||
Last updated: {{ cv.lastUpdated | date("PPP") }}
|
||||
Last updated: <time datetime="{{ cv.lastUpdated }}">{{ cv.lastUpdated | date("PPP") }}</time>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ permalink: "digest/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumb
|
||||
{{ d.label }}
|
||||
</h2>
|
||||
<p class="text-sm text-surface-500 dark:text-surface-400 mt-1">
|
||||
{{ d.startDate | dateDisplay }} – {{ d.endDate | dateDisplay }}
|
||||
<time datetime="{{ d.startDate | isoDate }}">{{ d.startDate | dateDisplay }}</time> – <time datetime="{{ d.endDate | isoDate }}">{{ d.endDate | dateDisplay }}</time>
|
||||
· {{ d.posts.length }} post{% if d.posts.length != 1 %}s{% endif %}
|
||||
</p>
|
||||
{% set typeLabels = [] %}
|
||||
|
||||
@@ -18,7 +18,7 @@ permalink: "digest/{{ digest.slug }}/"
|
||||
{{ digest.label }}
|
||||
</h1>
|
||||
<p class="text-surface-600 dark:text-surface-400 mb-6 sm:mb-8">
|
||||
{{ digest.startDate | dateDisplay }} – {{ digest.endDate | dateDisplay }}
|
||||
<time datetime="{{ digest.startDate | isoDate }}">{{ digest.startDate | dateDisplay }}</time> – <time datetime="{{ digest.endDate | isoDate }}">{{ digest.endDate | dateDisplay }}</time>
|
||||
<span class="text-sm">({{ digest.posts.length }} post{% if digest.posts.length != 1 %}s{% endif %})</span>
|
||||
</p>
|
||||
|
||||
|
||||
22
github.njk
22
github.njk
@@ -9,7 +9,7 @@ withSidebar: true
|
||||
<h1 class="text-2xl sm:text-3xl font-bold text-surface-900 dark:text-surface-100 mb-2">GitHub Activity</h1>
|
||||
<p class="text-surface-600 dark:text-surface-400">
|
||||
My open source contributions and starred repositories.
|
||||
<a href="https://github.com/{{ site.feeds.github }}" class="text-accent-600 dark:text-accent-400 hover:underline" target="_blank" rel="noopener">
|
||||
<a href="https://github.com/{{ site.feeds.github }}" class="text-emerald-600 dark:text-emerald-400 hover:underline" target="_blank" rel="noopener">
|
||||
Follow me on GitHub
|
||||
</a>
|
||||
</p>
|
||||
@@ -19,7 +19,7 @@ withSidebar: true
|
||||
{% if githubActivity.featured.length %}
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4 sm:mb-6 flex items-center gap-2">
|
||||
<svg class="w-6 h-6 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-6 h-6 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"/>
|
||||
</svg>
|
||||
Featured Projects
|
||||
@@ -27,7 +27,7 @@ withSidebar: true
|
||||
|
||||
<div class="grid gap-4 sm:gap-6 md:grid-cols-2">
|
||||
{% for repo in githubActivity.featured %}
|
||||
<article class="p-5 bg-gradient-to-br from-surface-50 to-white dark:from-surface-800 dark:to-surface-800 rounded-xl border-2 border-surface-200 dark:border-surface-700 shadow-sm">
|
||||
<article class="p-5 bg-surface-50 dark:bg-surface-800 rounded-xl border-2 border-surface-200 dark:border-surface-700 shadow-sm">
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<h3 class="font-bold text-lg text-surface-900 dark:text-surface-100">
|
||||
<a href="{{ repo.url }}" class="hover:text-emerald-600 dark:hover:text-emerald-400" target="_blank" rel="noopener">
|
||||
@@ -92,7 +92,7 @@ withSidebar: true
|
||||
{# Recent Commits Section #}
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4 sm:mb-6 flex items-center gap-2">
|
||||
<svg class="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-6 h-6 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||
</svg>
|
||||
Recent Commits
|
||||
@@ -111,7 +111,7 @@ withSidebar: true
|
||||
</a>
|
||||
<p class="text-xs text-surface-500 mt-1">
|
||||
<a href="{{ commit.repoUrl }}" class="hover:underline" target="_blank" rel="noopener">{{ commit.repo }}</a>
|
||||
· {{ commit.date | date("MMM d, yyyy") }}
|
||||
· <time datetime="{{ commit.date }}">{{ commit.date | date("MMM d, yyyy") }}</time>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -126,7 +126,7 @@ withSidebar: true
|
||||
{% if githubActivity.contributions.length %}
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4 sm:mb-6 flex items-center gap-2">
|
||||
<svg class="w-6 h-6 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-6 h-6 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z"/>
|
||||
</svg>
|
||||
Pull Requests & Issues
|
||||
@@ -136,7 +136,7 @@ withSidebar: true
|
||||
{% for item in githubActivity.contributions %}
|
||||
<div class="flex items-start gap-3 p-3 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700">
|
||||
{% if item.type == "pr" %}
|
||||
<span class="flex-shrink-0 px-2 py-1 text-xs font-medium bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 rounded">PR</span>
|
||||
<span class="flex-shrink-0 px-2 py-1 text-xs font-medium bg-emerald-100 dark:bg-emerald-900 text-emerald-800 dark:text-emerald-200 rounded">PR</span>
|
||||
{% else %}
|
||||
<span class="flex-shrink-0 px-2 py-1 text-xs font-medium bg-purple-100 dark:bg-purple-900 text-purple-800 dark:text-purple-200 rounded">Issue</span>
|
||||
{% endif %}
|
||||
@@ -147,7 +147,7 @@ withSidebar: true
|
||||
<p class="text-xs text-surface-500 mt-1">
|
||||
<a href="{{ item.repoUrl }}" class="hover:underline" target="_blank" rel="noopener">{{ item.repo }}</a>
|
||||
#{{ item.number }}
|
||||
· {{ item.date | date("MMM d, yyyy") }}
|
||||
· <time datetime="{{ item.date }}">{{ item.date | date("MMM d, yyyy") }}</time>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -203,7 +203,7 @@ withSidebar: true
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<a href="https://github.com/{{ site.feeds.github }}?tab=repositories" class="inline-block mt-4 text-accent-600 dark:text-accent-400 hover:underline">
|
||||
<a href="https://github.com/{{ site.feeds.github }}?tab=repositories" class="inline-block mt-4 text-emerald-600 dark:text-emerald-400 hover:underline">
|
||||
View all repositories →
|
||||
</a>
|
||||
{% else %}
|
||||
@@ -214,7 +214,7 @@ withSidebar: true
|
||||
{# Starred Repos Section #}
|
||||
<section>
|
||||
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4 sm:mb-6 flex items-center gap-2">
|
||||
<svg class="w-6 h-6 text-yellow-500" fill="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-6 h-6 text-emerald-500" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z"/>
|
||||
</svg>
|
||||
Starred Repositories
|
||||
@@ -260,7 +260,7 @@ withSidebar: true
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<a href="/github/starred/" class="inline-block mt-4 text-accent-600 dark:text-accent-400 hover:underline">
|
||||
<a href="/github/starred/" class="inline-block mt-4 text-emerald-600 dark:text-emerald-400 hover:underline">
|
||||
View all {{ githubStarred.totalCount | default(githubActivity.stars | length) }} starred repos →
|
||||
</a>
|
||||
{% else %}
|
||||
|
||||
@@ -20,18 +20,18 @@ permalink: /interactions/
|
||||
<div class="flex border-b border-surface-200 dark:border-surface-700 mb-6">
|
||||
<button
|
||||
@click="activeTab = 'outbound'"
|
||||
:class="activeTab === 'outbound' ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
|
||||
:class="activeTab === 'outbound' ? 'border-rose-500 text-rose-600 dark:text-rose-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
|
||||
class="px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors">
|
||||
My Activity
|
||||
<span class="ml-1 text-xs text-surface-400">(outbound)</span>
|
||||
</button>
|
||||
<button
|
||||
@click="activeTab = 'inbound'"
|
||||
:class="activeTab === 'inbound' ? 'border-accent-500 text-accent-600 dark:text-accent-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
|
||||
:class="activeTab === 'inbound' ? 'border-rose-500 text-rose-600 dark:text-rose-400' : 'border-transparent text-surface-500 hover:text-surface-700 dark:hover:text-surface-300'"
|
||||
class="px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors">
|
||||
Received
|
||||
<span class="ml-1 text-xs text-surface-400">(inbound)</span>
|
||||
<span x-show="totalInbound > 0" x-text="totalInbound" class="ml-1 px-1.5 py-0.5 text-xs bg-accent-100 dark:bg-accent-900 text-accent-700 dark:text-accent-300 rounded-full"></span>
|
||||
<span x-show="totalInbound > 0" x-text="totalInbound" class="ml-1 px-1.5 py-0.5 text-xs bg-rose-100 dark:bg-rose-900 text-rose-700 dark:text-rose-300 rounded-full"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -43,13 +43,13 @@ permalink: /interactions/
|
||||
{# Likes #}
|
||||
<a href="/likes/" class="block p-6 bg-surface-100 dark:bg-surface-800 rounded-lg hover:bg-surface-200 dark:hover:bg-surface-700 transition-colors group">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="p-3 bg-red-100 dark:bg-red-900/30 rounded-full">
|
||||
<svg class="w-6 h-6 text-red-500" fill="currentColor" viewBox="0 0 24 24">
|
||||
<div class="p-3 bg-rose-100 dark:bg-rose-900/30 rounded-full">
|
||||
<svg class="w-6 h-6 text-rose-500" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-red-600 dark:group-hover:text-red-400">Likes</h2>
|
||||
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-rose-600 dark:group-hover:text-rose-400">Likes</h2>
|
||||
<p class="text-sm text-surface-500">{{ collections.likes.length }} item{% if collections.likes.length != 1 %}s{% endif %}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -59,13 +59,13 @@ permalink: /interactions/
|
||||
{# Replies #}
|
||||
<a href="/replies/" class="block p-6 bg-surface-100 dark:bg-surface-800 rounded-lg hover:bg-surface-200 dark:hover:bg-surface-700 transition-colors group">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="p-3 bg-sky-100 dark:bg-sky-900/30 rounded-full">
|
||||
<svg class="w-6 h-6 text-sky-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<div class="p-3 bg-rose-100 dark:bg-rose-900/30 rounded-full">
|
||||
<svg class="w-6 h-6 text-rose-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-sky-600 dark:group-hover:text-sky-400">Replies</h2>
|
||||
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-rose-600 dark:group-hover:text-rose-400">Replies</h2>
|
||||
<p class="text-sm text-surface-500">{{ collections.replies.length }} item{% if collections.replies.length != 1 %}s{% endif %}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,13 +91,13 @@ permalink: /interactions/
|
||||
{# Reposts #}
|
||||
<a href="/reposts/" class="block p-6 bg-surface-100 dark:bg-surface-800 rounded-lg hover:bg-surface-200 dark:hover:bg-surface-700 transition-colors group">
|
||||
<div class="flex items-center gap-4 mb-4">
|
||||
<div class="p-3 bg-green-100 dark:bg-green-900/30 rounded-full">
|
||||
<svg class="w-6 h-6 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<div class="p-3 bg-rose-100 dark:bg-rose-900/30 rounded-full">
|
||||
<svg class="w-6 h-6 text-rose-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-green-600 dark:group-hover:text-green-400">Reposts</h2>
|
||||
<h2 class="text-lg font-semibold text-surface-900 dark:text-surface-100 group-hover:text-rose-600 dark:group-hover:text-rose-400">Reposts</h2>
|
||||
<p class="text-sm text-surface-500">{{ collections.reposts.length }} item{% if collections.reposts.length != 1 %}s{% endif %}</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -153,7 +153,7 @@ permalink: /interactions/
|
||||
|
||||
{# Loading state #}
|
||||
<div x-show="loading && !webmentions.length" class="text-center py-12">
|
||||
<div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-accent-500"></div>
|
||||
<div class="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-rose-500"></div>
|
||||
<p class="mt-4 text-surface-500">Loading webmentions...</p>
|
||||
</div>
|
||||
|
||||
@@ -181,7 +181,7 @@ permalink: /interactions/
|
||||
<div x-show="!notConfigured && (!loading || webmentions.length)" class="flex flex-wrap gap-2 mb-6">
|
||||
<button
|
||||
@click="filterType = 'all'"
|
||||
:class="filterType === 'all' ? 'bg-accent-500 text-white' : 'bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-300'"
|
||||
:class="filterType === 'all' ? 'bg-rose-500 text-white' : 'bg-surface-100 dark:bg-surface-800 text-surface-700 dark:text-surface-300'"
|
||||
class="px-3 py-1.5 text-sm rounded-full transition-colors">
|
||||
All <span x-text="'(' + totalInbound + ')'" class="text-xs opacity-75"></span>
|
||||
</button>
|
||||
@@ -271,7 +271,7 @@ permalink: /interactions/
|
||||
{# Target URL - which of my posts this is about #}
|
||||
<div class="mt-2 text-xs text-surface-500">
|
||||
<span>on </span>
|
||||
<a :href="wm['wm-target']" class="text-accent-600 dark:text-accent-400 hover:underline" x-text="formatTargetUrl(wm['wm-target'])"></a>
|
||||
<a :href="wm['wm-target']" class="text-rose-600 dark:text-rose-400 hover:underline" x-text="formatTargetUrl(wm['wm-target'])"></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -25,10 +25,10 @@ permalink: "likes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
|
||||
{% if paginatedLikes.length > 0 %}
|
||||
<ul class="post-list">
|
||||
{% for post in paginatedLikes %}
|
||||
<li class="h-entry post-card border-l-[3px] border-l-red-400 dark:border-l-red-500">
|
||||
<li class="h-entry post-card border-l-[3px] border-l-rose-400 dark:border-l-rose-500">
|
||||
<div class="post-header flex items-start gap-3">
|
||||
<div class="flex-shrink-0 mt-1">
|
||||
<svg class="w-5 h-5 text-red-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<svg class="w-5 h-5 text-rose-500" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
|
||||
</svg>
|
||||
</div>
|
||||
@@ -62,7 +62,7 @@ permalink: "likes/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNumbe
|
||||
{{ post.templateContent | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<a class="u-url text-sm text-red-600 dark:text-red-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
|
||||
<a class="u-url text-sm text-rose-600 dark:text-rose-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -14,7 +14,7 @@ withSidebar: true
|
||||
{% endif %}
|
||||
{% if funkwhaleActivity.instanceUrl and lastfmActivity.profileUrl %} and {% endif %}
|
||||
{% if lastfmActivity.profileUrl %}
|
||||
<a href="{{ lastfmActivity.profileUrl }}" class="text-red-600 dark:text-red-400 hover:underline" target="_blank" rel="noopener">Last.fm</a>
|
||||
<a href="{{ lastfmActivity.profileUrl }}" class="text-purple-600 dark:text-purple-400 hover:underline" target="_blank" rel="noopener">Last.fm</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</header>
|
||||
@@ -75,11 +75,11 @@ withSidebar: true
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-purple-500/20 text-purple-700 dark:text-purple-400 rounded-full">Funkwhale</span>
|
||||
{% if fwNowPlaying.status == 'now-playing' %}
|
||||
<span class="inline-flex items-center gap-1.5 px-2 py-1 text-xs font-medium bg-green-500/20 text-green-700 dark:text-green-400 rounded-full">
|
||||
<span class="inline-flex items-center gap-1.5 px-2 py-1 text-xs font-medium bg-purple-500/20 text-purple-700 dark:text-purple-400 rounded-full">
|
||||
<span class="flex gap-0.5 items-end h-3">
|
||||
<span class="w-0.5 bg-green-500 animate-pulse" style="height: 30%; animation-delay: 0s;"></span>
|
||||
<span class="w-0.5 bg-green-500 animate-pulse" style="height: 70%; animation-delay: 0.2s;"></span>
|
||||
<span class="w-0.5 bg-green-500 animate-pulse" style="height: 50%; animation-delay: 0.4s;"></span>
|
||||
<span class="w-0.5 bg-purple-500 animate-pulse" style="height: 30%; animation-delay: 0s;"></span>
|
||||
<span class="w-0.5 bg-purple-500 animate-pulse" style="height: 70%; animation-delay: 0.2s;"></span>
|
||||
<span class="w-0.5 bg-purple-500 animate-pulse" style="height: 50%; animation-delay: 0.4s;"></span>
|
||||
</span>
|
||||
Now Playing
|
||||
</span>
|
||||
@@ -125,13 +125,13 @@ withSidebar: true
|
||||
|
||||
<div class="flex-1 min-w-0 w-full sm:w-auto">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-red-500/20 text-red-700 dark:text-red-400 rounded-full">Last.fm</span>
|
||||
<span class="inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-purple-500/20 text-purple-700 dark:text-purple-400 rounded-full">Last.fm</span>
|
||||
{% if lfmNowPlaying.status == 'now-playing' %}
|
||||
<span class="inline-flex items-center gap-1.5 px-2 py-1 text-xs font-medium bg-green-500/20 text-green-700 dark:text-green-400 rounded-full">
|
||||
<span class="inline-flex items-center gap-1.5 px-2 py-1 text-xs font-medium bg-purple-500/20 text-purple-700 dark:text-purple-400 rounded-full">
|
||||
<span class="flex gap-0.5 items-end h-3">
|
||||
<span class="w-0.5 bg-green-500 animate-pulse" style="height: 30%; animation-delay: 0s;"></span>
|
||||
<span class="w-0.5 bg-green-500 animate-pulse" style="height: 70%; animation-delay: 0.2s;"></span>
|
||||
<span class="w-0.5 bg-green-500 animate-pulse" style="height: 50%; animation-delay: 0.4s;"></span>
|
||||
<span class="w-0.5 bg-purple-500 animate-pulse" style="height: 30%; animation-delay: 0s;"></span>
|
||||
<span class="w-0.5 bg-purple-500 animate-pulse" style="height: 70%; animation-delay: 0.2s;"></span>
|
||||
<span class="w-0.5 bg-purple-500 animate-pulse" style="height: 50%; animation-delay: 0.4s;"></span>
|
||||
</span>
|
||||
Now Playing
|
||||
</span>
|
||||
@@ -141,13 +141,13 @@ withSidebar: true
|
||||
</span>
|
||||
{% endif %}
|
||||
{% if lfmNowPlaying.loved %}
|
||||
<span class="text-red-500" title="Loved">♥</span>
|
||||
<span class="text-purple-500" title="Loved">♥</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<h2 class="text-lg sm:text-xl font-bold text-surface-900 dark:text-surface-100 truncate">
|
||||
{% if lfmNowPlaying.trackUrl %}
|
||||
<a href="{{ lfmNowPlaying.trackUrl }}" class="hover:text-red-600 dark:hover:text-red-400" target="_blank" rel="noopener">{{ lfmNowPlaying.track }}</a>
|
||||
<a href="{{ lfmNowPlaying.trackUrl }}" class="hover:text-purple-600 dark:hover:text-purple-400" target="_blank" rel="noopener">{{ lfmNowPlaying.track }}</a>
|
||||
{% else %}
|
||||
{{ lfmNowPlaying.track }}
|
||||
{% endif %}
|
||||
@@ -169,7 +169,7 @@ withSidebar: true
|
||||
{% if funkwhaleActivity.stats or lastfmActivity.stats %}
|
||||
<section class="mb-12">
|
||||
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4 sm:mb-6 flex items-center gap-2">
|
||||
<svg class="w-6 h-6 text-accent-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-6 h-6 text-purple-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
|
||||
</svg>
|
||||
Listening Statistics
|
||||
@@ -217,9 +217,9 @@ withSidebar: true
|
||||
|
||||
{# Last.fm Stats #}
|
||||
{% if lastfmActivity.stats %}
|
||||
<div x-show="activeSource === 'all' || activeSource === 'lastfm'" class="bg-surface-50 dark:bg-surface-800 rounded-xl p-6 border border-red-200 dark:border-red-800">
|
||||
<h3 class="text-lg font-semibold text-red-700 dark:text-red-400 mb-4 flex items-center gap-2">
|
||||
<span class="w-3 h-3 rounded-full bg-red-500"></span>
|
||||
<div x-show="activeSource === 'all' || activeSource === 'lastfm'" class="bg-surface-50 dark:bg-surface-800 rounded-xl p-6 border border-purple-200 dark:border-purple-800">
|
||||
<h3 class="text-lg font-semibold text-purple-700 dark:text-purple-400 mb-4 flex items-center gap-2">
|
||||
<span class="w-3 h-3 rounded-full bg-purple-500"></span>
|
||||
Last.fm
|
||||
</h3>
|
||||
<div class="grid grid-cols-3 gap-4 text-center">
|
||||
@@ -324,7 +324,7 @@ withSidebar: true
|
||||
{% if lastfmActivity.scrobbles.length %}
|
||||
<div x-show="activeSource === 'all' || activeSource === 'lastfm'">
|
||||
{% for scrobble in lastfmActivity.scrobbles | head(10) %}
|
||||
<div class="flex items-center gap-4 p-3 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 hover:border-red-400 dark:hover:border-red-600 transition-colors mb-2">
|
||||
<div class="flex items-center gap-4 p-3 bg-surface-50 dark:bg-surface-800 rounded-lg border border-surface-200 dark:border-surface-700 hover:border-purple-400 dark:hover:border-purple-600 transition-colors mb-2">
|
||||
{% if scrobble.coverUrl %}
|
||||
<img src="{{ scrobble.coverUrl }}" alt="" class="w-12 h-12 rounded object-cover flex-shrink-0" loading="lazy" eleventy:ignore>
|
||||
{% else %}
|
||||
@@ -338,19 +338,19 @@ withSidebar: true
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-medium text-surface-900 dark:text-surface-100 truncate">
|
||||
{% if scrobble.trackUrl %}
|
||||
<a href="{{ scrobble.trackUrl }}" class="hover:text-red-600 dark:hover:text-red-400" target="_blank" rel="noopener">{{ scrobble.track }}</a>
|
||||
<a href="{{ scrobble.trackUrl }}" class="hover:text-purple-600 dark:hover:text-purple-400" target="_blank" rel="noopener">{{ scrobble.track }}</a>
|
||||
{% else %}
|
||||
{{ scrobble.track }}
|
||||
{% endif %}
|
||||
{% if scrobble.loved %}
|
||||
<span class="text-red-500 ml-1" title="Loved">♥</span>
|
||||
<span class="text-purple-500 ml-1" title="Loved">♥</span>
|
||||
{% endif %}
|
||||
</h3>
|
||||
<p class="text-sm text-surface-600 dark:text-surface-400 truncate">{{ scrobble.artist }}</p>
|
||||
</div>
|
||||
|
||||
<div class="text-right flex-shrink-0">
|
||||
<span class="inline-block px-2 py-0.5 text-xs font-medium bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-400 rounded-full mb-1">Last.fm</span>
|
||||
<span class="inline-block px-2 py-0.5 text-xs font-medium bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 rounded-full mb-1">Last.fm</span>
|
||||
<span class="text-xs text-surface-500 block">{{ scrobble.relativeTime }}</span>
|
||||
<button
|
||||
class="share-post-btn mt-1"
|
||||
@@ -387,7 +387,7 @@ withSidebar: true
|
||||
{% if lastfmActivity.loved.length %}
|
||||
<section class="mb-12" x-show="activeSource === 'all' || activeSource === 'lastfm'">
|
||||
<h2 class="text-xl sm:text-2xl font-bold text-surface-900 dark:text-surface-100 mb-4 sm:mb-6 flex items-center gap-2">
|
||||
<svg class="w-6 h-6 text-red-500" fill="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-6 h-6 text-purple-500" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
|
||||
</svg>
|
||||
Loved Tracks
|
||||
@@ -401,7 +401,7 @@ withSidebar: true
|
||||
<img src="{{ track.coverUrl }}" alt="" class="w-14 h-14 rounded object-cover flex-shrink-0" loading="lazy" eleventy:ignore>
|
||||
{% else %}
|
||||
<div class="w-14 h-14 rounded bg-surface-200 dark:bg-surface-700 flex items-center justify-center flex-shrink-0">
|
||||
<svg class="w-6 h-6 text-red-400" fill="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-6 h-6 text-purple-400" fill="currentColor" viewBox="0 0 24 24">
|
||||
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/>
|
||||
</svg>
|
||||
</div>
|
||||
@@ -410,7 +410,7 @@ withSidebar: true
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="font-medium text-surface-900 dark:text-surface-100 truncate">
|
||||
{% if track.trackUrl %}
|
||||
<a href="{{ track.trackUrl }}" class="hover:text-red-600 dark:hover:text-red-400" target="_blank" rel="noopener">{{ track.track }}</a>
|
||||
<a href="{{ track.trackUrl }}" class="hover:text-purple-600 dark:hover:text-purple-400" target="_blank" rel="noopener">{{ track.track }}</a>
|
||||
{% else %}
|
||||
{{ track.track }}
|
||||
{% endif %}
|
||||
@@ -418,7 +418,7 @@ withSidebar: true
|
||||
<p class="text-sm text-surface-600 dark:text-surface-400 truncate">{{ track.artist }}</p>
|
||||
</div>
|
||||
|
||||
<span class="text-red-500 flex-shrink-0">♥</span>
|
||||
<span class="text-purple-500 flex-shrink-0">♥</span>
|
||||
<button
|
||||
class="share-post-btn flex-shrink-0"
|
||||
data-share-url="{{ track.trackUrl }}"
|
||||
|
||||
2
news.njk
2
news.njk
@@ -190,7 +190,7 @@ withSidebar: true
|
||||
{# Card View #}
|
||||
<div x-show="viewMode === 'card'" class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<template x-for="item in filteredItems" :key="item.id">
|
||||
<article class="bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 overflow-hidden hover:shadow-lg transition-shadow">
|
||||
<article class="bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 shadow-sm overflow-hidden hover:border-surface-400 dark:hover:border-surface-500 transition-colors">
|
||||
<div x-show="item.imageUrl" class="aspect-video bg-surface-100 dark:bg-surface-700">
|
||||
<img
|
||||
:src="item.imageUrl"
|
||||
|
||||
16
podroll.njk
16
podroll.njk
@@ -19,7 +19,7 @@ permalink: /podroll/
|
||||
</p>
|
||||
<p class="text-xs text-surface-500 mt-2" x-show="status?.episodes?.lastSync">
|
||||
Last synced: <span x-text="formatDate(status?.episodes?.lastSync, 'full')"></span>
|
||||
<button @click="refresh()" class="ml-2 text-accent-600 hover:text-accent-700 dark:text-accent-400" :disabled="loading">
|
||||
<button @click="refresh()" class="ml-2 text-orange-600 hover:text-orange-700 dark:text-orange-400" :disabled="loading">
|
||||
<svg class="w-3 h-3 inline" :class="{ 'animate-spin': loading }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
||||
</svg>
|
||||
@@ -32,7 +32,7 @@ permalink: /podroll/
|
||||
<div class="main-content">
|
||||
{# Loading State #}
|
||||
<div x-show="loading && episodes.length === 0" class="text-center py-12">
|
||||
<svg class="w-8 h-8 mx-auto text-accent-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24">
|
||||
<svg class="w-8 h-8 mx-auto text-orange-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
@@ -50,7 +50,7 @@ permalink: /podroll/
|
||||
<div class="relative">
|
||||
<select
|
||||
x-model="filterPodcast"
|
||||
class="w-full sm:w-auto appearance-none bg-surface-100 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg px-4 py-2 pr-10 text-sm text-surface-700 dark:text-surface-300 focus:outline-none focus:ring-2 focus:ring-accent-500"
|
||||
class="w-full sm:w-auto appearance-none bg-surface-100 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg px-4 py-2 pr-10 text-sm text-surface-700 dark:text-surface-300 focus:outline-none focus:ring-2 focus:ring-orange-500"
|
||||
>
|
||||
<option value="all">All Podcasts</option>
|
||||
<template x-for="source in sortedSources" :key="source.title">
|
||||
@@ -66,12 +66,12 @@ permalink: /podroll/
|
||||
{# Episodes List #}
|
||||
<div x-show="episodes.length > 0" class="space-y-4">
|
||||
<template x-for="episode in filteredEpisodes" :key="episode.id">
|
||||
<article class="bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-4 sm:p-6 hover:border-accent-400 dark:hover:border-accent-600 transition-colors">
|
||||
<article class="bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-4 sm:p-6 hover:border-orange-400 dark:hover:border-orange-600 transition-colors">
|
||||
{# Episode Header #}
|
||||
<div class="flex items-start gap-4 mb-4">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h2 class="font-semibold text-lg text-surface-900 dark:text-surface-100 mb-1">
|
||||
<a :href="episode.url" class="hover:text-accent-600 dark:hover:text-accent-400" target="_blank" rel="noopener" x-text="episode.title"></a>
|
||||
<a :href="episode.url" class="hover:text-orange-600 dark:hover:text-orange-400" target="_blank" rel="noopener" x-text="episode.title"></a>
|
||||
</h2>
|
||||
<div class="flex flex-wrap items-center gap-2 text-sm text-surface-500">
|
||||
<a
|
||||
@@ -121,7 +121,7 @@ permalink: /podroll/
|
||||
<div class="flex flex-wrap items-center gap-3 mt-4 pt-4 border-t border-surface-200 dark:border-surface-700">
|
||||
<a
|
||||
:href="episode.url"
|
||||
class="inline-flex items-center gap-2 text-sm text-accent-600 hover:text-accent-700 dark:text-accent-400"
|
||||
class="inline-flex items-center gap-2 text-sm text-orange-600 hover:text-orange-700 dark:text-orange-400"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
@@ -175,7 +175,7 @@ permalink: /podroll/
|
||||
<button
|
||||
@click="loadMore()"
|
||||
:disabled="loadingMore"
|
||||
class="px-6 py-3 bg-accent-600 hover:bg-accent-700 text-white rounded-lg text-sm font-medium transition-colors disabled:opacity-50"
|
||||
class="px-6 py-3 bg-orange-600 hover:bg-orange-700 text-white rounded-lg text-sm font-medium transition-colors disabled:opacity-50"
|
||||
>
|
||||
<span x-show="!loadingMore">Load More Episodes</span>
|
||||
<span x-show="loadingMore" class="flex items-center gap-2">
|
||||
@@ -232,7 +232,7 @@ permalink: /podroll/
|
||||
</div>
|
||||
<a
|
||||
:href="source.xmlUrl"
|
||||
class="opacity-0 group-hover:opacity-100 text-surface-400 hover:text-accent-600 transition-opacity"
|
||||
class="opacity-0 group-hover:opacity-100 text-surface-400 hover:text-orange-600 transition-opacity"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
title="RSS Feed"
|
||||
|
||||
@@ -6,7 +6,7 @@ permalink: /readlater/
|
||||
<div class="readlater-page" x-data="readlaterApp()" x-init="init()">
|
||||
<header class="mb-6 sm:mb-8">
|
||||
<h1 class="text-2xl sm:text-3xl md:text-4xl font-bold text-surface-900 dark:text-surface-100 mb-2">
|
||||
<svg class="w-8 h-8 inline-block mr-2 text-accent-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-8 h-8 inline-block mr-2 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
|
||||
</svg>
|
||||
Reading List
|
||||
@@ -21,7 +21,7 @@ permalink: /readlater/
|
||||
<div class="main-content">
|
||||
{# Loading State #}
|
||||
<div x-show="loading" class="text-center py-12">
|
||||
<svg class="w-8 h-8 mx-auto text-accent-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24">
|
||||
<svg class="w-8 h-8 mx-auto text-orange-600 animate-spin mb-4" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
@@ -41,7 +41,7 @@ permalink: /readlater/
|
||||
<select
|
||||
x-model="selectedSource"
|
||||
@change="fetchData()"
|
||||
class="appearance-none bg-surface-50 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg pl-3 pr-8 py-2 text-sm focus:ring-2 focus:ring-accent-500 focus:border-accent-500"
|
||||
class="appearance-none bg-surface-50 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg pl-3 pr-8 py-2 text-sm focus:ring-2 focus:ring-orange-500 focus:border-orange-500"
|
||||
>
|
||||
<option value="">All sources</option>
|
||||
<template x-for="src in sources" :key="src">
|
||||
@@ -58,7 +58,7 @@ permalink: /readlater/
|
||||
x-model.debounce.300ms="searchQuery"
|
||||
@input="fetchData()"
|
||||
placeholder="Search..."
|
||||
class="w-full bg-surface-50 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg pl-9 pr-3 py-2 text-sm focus:ring-2 focus:ring-accent-500 focus:border-accent-500"
|
||||
class="w-full bg-surface-50 dark:bg-surface-800 border border-surface-300 dark:border-surface-600 rounded-lg pl-9 pr-3 py-2 text-sm focus:ring-2 focus:ring-orange-500 focus:border-orange-500"
|
||||
/>
|
||||
<svg class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-surface-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
||||
@@ -80,16 +80,16 @@ permalink: /readlater/
|
||||
{# Items List #}
|
||||
<div x-show="!loading" class="space-y-4">
|
||||
<template x-for="item in items" :key="item.id">
|
||||
<article class="bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-4 sm:p-5 hover:border-accent-400 dark:hover:border-accent-600 transition-colors">
|
||||
<article class="bg-surface-50 dark:bg-surface-800 rounded-xl border border-surface-200 dark:border-surface-700 p-4 sm:p-5 hover:border-orange-400 dark:hover:border-orange-600 transition-colors">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="flex-shrink-0 w-10 h-10 rounded-lg bg-gradient-to-br from-accent-400 to-accent-600 flex items-center justify-center">
|
||||
<div class="flex-shrink-0 w-10 h-10 rounded-lg bg-gradient-to-br from-orange-400 to-orange-600 flex items-center justify-center">
|
||||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 5a2 2 0 012-2h10a2 2 0 012 2v16l-7-3.5L5 21V5z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h2 class="font-semibold text-surface-900 dark:text-surface-100 mb-1">
|
||||
<a :href="item.url" class="hover:text-accent-600 dark:hover:text-accent-400 transition-colors" target="_blank" rel="noopener" x-text="item.title"></a>
|
||||
<a :href="item.url" class="hover:text-orange-600 dark:hover:text-orange-400 transition-colors" target="_blank" rel="noopener" x-text="item.title"></a>
|
||||
</h2>
|
||||
<div class="flex flex-wrap items-center gap-2 text-sm text-surface-500">
|
||||
<span
|
||||
@@ -106,7 +106,7 @@ permalink: /readlater/
|
||||
<div class="flex flex-wrap items-center gap-3 mt-3 pt-3 border-t border-surface-200 dark:border-surface-700">
|
||||
<a
|
||||
:href="item.url"
|
||||
class="inline-flex items-center gap-2 text-sm text-accent-600 hover:text-accent-700 dark:text-accent-400"
|
||||
class="inline-flex items-center gap-2 text-sm text-orange-600 hover:text-orange-700 dark:text-orange-400"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
@@ -147,11 +147,11 @@ permalink: /readlater/
|
||||
<h3 class="widget-title">Stats</h3>
|
||||
<div class="grid grid-cols-2 gap-3 text-center">
|
||||
<div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg">
|
||||
<span class="text-2xl font-bold text-accent-600 dark:text-accent-400 block" x-text="items.length"></span>
|
||||
<span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="items.length"></span>
|
||||
<span class="text-xs text-surface-500 uppercase">Saved</span>
|
||||
</div>
|
||||
<div class="p-3 bg-surface-50 dark:bg-surface-800 rounded-lg">
|
||||
<span class="text-2xl font-bold text-accent-600 dark:text-accent-400 block" x-text="sources.length"></span>
|
||||
<span class="text-2xl font-bold text-orange-600 dark:text-orange-400 block" x-text="sources.length"></span>
|
||||
<span class="text-xs text-surface-500 uppercase">Sources</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -160,7 +160,7 @@ permalink: /readlater/
|
||||
{# Sources Quick Filter #}
|
||||
<div class="widget" x-show="sources.length > 0">
|
||||
<h3 class="widget-title flex items-center gap-2">
|
||||
<svg class="w-5 h-5 text-accent-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg class="w-5 h-5 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"/>
|
||||
</svg>
|
||||
Sources
|
||||
@@ -169,7 +169,7 @@ permalink: /readlater/
|
||||
<li>
|
||||
<button
|
||||
@click="selectedSource = ''; fetchData()"
|
||||
:class="selectedSource === '' ? 'bg-accent-100 dark:bg-accent-900/30 text-accent-700 dark:text-accent-400' : 'hover:bg-surface-100 dark:hover:bg-surface-700'"
|
||||
:class="selectedSource === '' ? 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400' : 'hover:bg-surface-100 dark:hover:bg-surface-700'"
|
||||
class="w-full text-left px-3 py-2 rounded-lg text-sm transition-colors"
|
||||
>
|
||||
All
|
||||
@@ -179,7 +179,7 @@ permalink: /readlater/
|
||||
<li>
|
||||
<button
|
||||
@click="selectedSource = src; fetchData()"
|
||||
:class="selectedSource === src ? 'bg-accent-100 dark:bg-accent-900/30 text-accent-700 dark:text-accent-400' : 'hover:bg-surface-100 dark:hover:bg-surface-700'"
|
||||
:class="selectedSource === src ? 'bg-orange-100 dark:bg-orange-900/30 text-orange-700 dark:text-orange-400' : 'hover:bg-surface-100 dark:hover:bg-surface-700'"
|
||||
class="w-full text-left px-3 py-2 rounded-lg text-sm transition-colors"
|
||||
x-text="src"
|
||||
></button>
|
||||
|
||||
@@ -25,17 +25,17 @@ permalink: "replies/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
|
||||
{% if paginatedReplies.length > 0 %}
|
||||
<ul class="post-list">
|
||||
{% for post in paginatedReplies %}
|
||||
<li class="h-entry post-card border-l-[3px] border-l-sky-400 dark:border-l-sky-500">
|
||||
<li class="h-entry post-card border-l-[3px] border-l-rose-400 dark:border-l-rose-500">
|
||||
<div class="post-header flex items-start gap-3">
|
||||
<div class="flex-shrink-0 mt-1">
|
||||
<svg class="w-5 h-5 text-sky-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<svg class="w-5 h-5 text-rose-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
{% if post.data.title %}
|
||||
<h2 class="p-name text-lg font-semibold text-surface-900 dark:text-surface-100 mb-2">
|
||||
<a class="hover:text-sky-600 dark:hover:text-sky-400" href="{{ post.url }}">{{ post.data.title }}</a>
|
||||
<a class="hover:text-rose-600 dark:hover:text-rose-400" href="{{ post.url }}">{{ post.data.title }}</a>
|
||||
</h2>
|
||||
{% endif %}
|
||||
<div class="post-meta">
|
||||
@@ -91,7 +91,7 @@ permalink: "replies/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
|
||||
<div class="e-content prose dark:prose-invert prose-sm mt-3 max-w-none">
|
||||
{{ post.templateContent | safe }}
|
||||
</div>
|
||||
<a class="u-url text-sm text-sky-600 dark:text-sky-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
|
||||
<a class="u-url text-sm text-rose-600 dark:text-rose-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
@@ -25,17 +25,17 @@ permalink: "reposts/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
|
||||
{% if paginatedReposts.length > 0 %}
|
||||
<ul class="post-list">
|
||||
{% for post in paginatedReposts %}
|
||||
<li class="h-entry post-card border-l-[3px] border-l-green-400 dark:border-l-green-500">
|
||||
<li class="h-entry post-card border-l-[3px] border-l-rose-400 dark:border-l-rose-500">
|
||||
<div class="post-header flex items-start gap-3">
|
||||
<div class="flex-shrink-0 mt-1">
|
||||
<svg class="w-5 h-5 text-green-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<svg class="w-5 h-5 text-rose-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="flex-1 min-w-0">
|
||||
{% if post.data.title %}
|
||||
<h2 class="p-name text-lg font-semibold text-surface-900 dark:text-surface-100 mb-2">
|
||||
<a class="hover:text-green-600 dark:hover:text-green-400" href="{{ post.url }}">{{ post.data.title }}</a>
|
||||
<a class="hover:text-rose-600 dark:hover:text-rose-400" href="{{ post.url }}">{{ post.data.title }}</a>
|
||||
</h2>
|
||||
{% endif %}
|
||||
<div class="post-meta">
|
||||
@@ -67,7 +67,7 @@ permalink: "reposts/{% if pagination.pageNumber > 0 %}page/{{ pagination.pageNum
|
||||
{{ post.templateContent | safe }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<a class="u-url text-sm text-green-600 dark:text-green-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
|
||||
<a class="u-url text-sm text-rose-600 dark:text-rose-400 hover:underline mt-3 inline-block" href="{{ post.url }}">Permalink</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user