diff --git a/.agents/skills/designing-beautiful-websites/README.md b/.agents/skills/designing-beautiful-websites/README.md new file mode 100644 index 0000000..16b74ad --- /dev/null +++ b/.agents/skills/designing-beautiful-websites/README.md @@ -0,0 +1,13 @@ +# designing-beautiful-websites + +Designs UX/UI for websites and web apps: UX strategy, information architecture, user flows, wireframes, UI systems, and polished visual design. + +## Quick install + +```bash +npx skills add https://github.com/tristanmanchester/agent-skills --skill designing-beautiful-websites +``` + +## Usage + +See `SKILL.md` for the workflow and deliverables. diff --git a/.agents/skills/designing-beautiful-websites/SKILL.md b/.agents/skills/designing-beautiful-websites/SKILL.md new file mode 100644 index 0000000..035e26f --- /dev/null +++ b/.agents/skills/designing-beautiful-websites/SKILL.md @@ -0,0 +1,359 @@ +--- +name: designing-beautiful-websites +description: >- + Designs UX/UI for websites and web apps: UX strategy, information architecture, + user flows, wireframes, UI systems, component specs, and polished visual design. + Use when the user asks to design, redesign, modernise, critique, or improve a + website, landing page, web app interface, design system, style guide, or UI kit. +allowed-tools: Read,Write,Bash(python:*) +--- + +# Designing Beautiful Websites + +> **Core philosophy:** Make the next action obvious. Build from user goals upward. Systemise visuals. Validate early. + +## Why this exists +Websites fail when they look “nice” but: +- don’t match user goals, +- hide key actions, +- require too much thinking, +- or are visually inconsistent. + +This skill turns vague requests like “make it look better” into a **repeatable workflow** that produces: +- clear structure, +- usable interactions, +- and a cohesive visual system. + +## What “done” looks like +Deliverables should be **usable by builders** (engineers, no-code builders, future agents): +- **Design brief**: users, goals, constraints, success metrics. +- **IA + flows**: sitemap (or nav model), and 1–3 key user journeys. +- **Layout + wireframes**: responsive page structure, component inventory. +- **Visual system**: tokens (type, spacing, colour, radius, shadow), and usage rules. +- **Component specs**: states, behaviour, empty/loading/error. +- **QA notes**: accessibility, responsiveness, edge cases. + +If time is limited, prioritise: **clarity + hierarchy + consistency + accessibility**. + +## Quick start workflow +Copy this checklist into the working notes and tick it off: + +- [ ] **0. Inputs**: goal, audience, content, constraints, brand signals. +- [ ] **1. Strategy**: user goals + business goals + success metrics. +- [ ] **2. Scope**: pages/features/content; prioritise “key paths”. +- [ ] **3. Structure**: IA + navigation model + flows. +- [ ] **4. Skeleton**: wireframes + component inventory + responsive layout. +- [ ] **5. Surface**: visual system + page comps + states. +- [ ] **6. Validate**: usability pass + accessibility pass + consistency pass. +- [ ] **7. Hand-off**: tokens + component specs + implementation notes. + +**Default rule:** do not jump to surface polish until structure and skeleton are believable. + +## Non‑negotiables +### 1) Reduce thinking +Design so users rarely wonder: +- “Where am I?” +- “What do I do next?” +- “Is that clickable?” +- “Why did they call it that?” + +Prefer **obvious** over clever. + +### 2) Use conventions aggressively +Use familiar patterns unless there is a measured reason to deviate. +Unusual UI is a tax on every user interaction. + +### 3) Clear visual hierarchy +Every screen must answer (at a glance): +- what this page is, +- what the primary action is, +- where the navigation is, +- what is secondary. + +### 4) Grouping must be unambiguous +If spacing is doing grouping work: +- there must be **more space around groups than within groups**. + +### 5) Feedback and forgiveness +Users should: +- see results of actions quickly, +- understand system status, +- and recover via undo/back/cancel where possible. + +Prefer preventing errors over scolding users. + +### 6) Accessibility is part of “beautiful” +Good aesthetics survive: +- keyboard-only use, +- low vision, +- colour‑blindness, +- small screens, +- slow networks. + +## Default outputs format +When responding, produce: +1. **Design brief** (bullets) +2. **IA + key flows** (bullets + simple diagrams if useful) +3. **Component inventory** (table or list) +4. **Design tokens** (CSS variables or JSON) +5. **Page-level guidance** (for each page/section) +6. **States & edge cases** +7. **Implementation notes** (HTML structure, CSS approach, ARIA, etc.) + +If the user asked for a critique/audit, output: +- issues (grouped by severity), +- fixes, +- and a “next iteration plan”. + +## The workflow in practice +### Step 0 — Gather inputs (fast) +Ask only what’s needed; otherwise assume and state assumptions. + +Minimum questions: +- **Primary user**: who is this for? +- **Primary goal**: what must they do? +- **Business goal**: what does success look like? +- **Content**: what is real copy/data? +- **Brand signals**: existing colours/logo/type/voice? +- **Constraints**: tech stack, deadline, accessibility level. + +If inputs are missing, create a **working brief** with explicit assumptions. + +### Step 1 — Strategy (align intent) +Produce: +- primary + secondary user goals, +- business objectives, +- success metrics, +- constraints/risk. + +### Step 2 — Scope (decide what exists) +Define: +- pages/screens, +- features, +- content requirements, +- and what is out of scope. + +Pick 1–3 **key paths** (the journeys that matter most). Optimise these first. + +### Step 3 — Structure (make it findable) +Create: +- sitemap / nav model (global + local nav), +- page purpose statements, +- user flows for key paths. + +Rule: navigation labels should be self‑evident; avoid internal jargon. + +### Step 4 — Skeleton (arrange the UI) +Create: +- wireframes per page, +- component inventory, +- layout constraints (container widths, grids, spacing rhythm), +- and priority order per breakpoint. + +Rule: start with the **feature/content**, not the “app shell”. + +### Step 5 — Surface (make it beautiful) +Build a consistent system: +- spacing + sizing scale, +- typography scale, +- colour palette + shades, +- radius + border rules, +- elevation/shadow scale, +- icon + illustration style, +- motion rules (optional). + +Apply to page comps. + +### Step 6 — Validate (fast loops) +Run these checks: +- **Glance test (5–10 seconds):** can someone tell what this is and what to do? +- **Key‑path walkthrough:** can a first‑time user complete the main task? +- **Consistency pass:** are tokens respected? is hierarchy consistent? +- **Accessibility pass:** contrast, focus states, semantics, error messaging. + +### Step 7 — Hand-off (make it buildable) +Provide: +- tokens, +- component specs (states + spacing + behaviour), +- responsive rules, +- and edge cases. + +## Default starter system +Use this system unless the project already has one. + +### Spacing & sizing scale (px) +Use a **non-linear** scale so choices are easy: +`0, 4, 8, 12, 16, 24, 32, 48, 64, 96, 128` + +Rules: +- pick from the scale; avoid “one-off” numbers. +- for grouping: **inside-group spacing < between-group spacing**. + +### Typography scale (px) +Keep it tight: 6–8 sizes is enough. + +Suggested: +- `12` caption +- `14` small/body-secondary +- `16` body +- `20` subheading +- `24` h3 +- `30` h2 +- `40` h1/hero + +Rules: +- default body line-height ~ `1.5–1.7`. +- limit line length for reading (~45–80 characters). +- use weight/colour/spacing before adding new sizes. + +### Colour system +- define neutrals (backgrounds + text), one primary, and semantic accents. +- define **shades up front** (e.g., 100–900), don’t generate ad-hoc lightens/darkens. +- “Grey” can be warm or cool; keep a consistent temperature. + +Contrast rules: +- normal text target: **≥ 4.5:1** +- large text target: **≥ 3:1** + +### Elevation / shadow system +Use 3–5 shadow levels that map to meaning: +- 1: buttons/cards (subtle) +- 2: popovers/menus +- 3: sticky headers +- 4: modals +- 5: high priority overlays + +### Borders +Prefer: +- spacing, +- subtle shadows, +- or small background changes +over heavy borders. + +### Empty states +Empty states are a first impression. +They must: +- explain what’s empty, +- why it matters, +- and what to do next. + +## Progressive disclosure +Use these reference files when deeper detail is needed: +- **Workflow & deliverables** → [references/WORKFLOW.md](references/WORKFLOW.md) +- **Page patterns** → [references/PAGE-PATTERNS.md](references/PAGE-PATTERNS.md) +- **Audit / critique** → [references/DESIGN-AUDIT.md](references/DESIGN-AUDIT.md) +- **Usability & navigation** → [references/USABILITY.md](references/USABILITY.md) +- **Visual design systems** → [references/VISUAL-DESIGN.md](references/VISUAL-DESIGN.md) +- **Interaction & forms** → [references/INTERACTION-DESIGN.md](references/INTERACTION-DESIGN.md) +- **Information architecture** → [references/INFORMATION-ARCHITECTURE.md](references/INFORMATION-ARCHITECTURE.md) +- **Content & microcopy** → [references/CONTENT-COPY.md](references/CONTENT-COPY.md) +- **Responsive rules** → [references/RESPONSIVE.md](references/RESPONSIVE.md) +- **Accessibility** → [references/ACCESSIBILITY.md](references/ACCESSIBILITY.md) +- **Checklists & templates** → [references/CHECKLISTS.md](references/CHECKLISTS.md) + +## Quick search +If running locally: +```bash +grep -i "empty state\|hierarchy\|spacing" -n references/*.md +``` + +## THE EXACT PROMPT — UX/UI plan + +``` +You are designing a website UI/UX. + +1) Write a crisp design brief (users, goals, constraints, success metrics). +2) Define information architecture + navigation model. +3) Identify 1–3 key user paths; write step-by-step flows. +4) Produce a component inventory for the key pages. +5) Propose a design token system (spacing, type, colour, radius, shadow) with rules. +6) Describe page layouts (mobile-first) and key interactions. +7) List empty/loading/error states and edge cases. +8) Run a usability + accessibility + consistency pass; revise. + +Output must be specific and implementable. +Avoid vague advice. +``` + +## THE EXACT PROMPT — Visual polish pass + +``` +Review this UI for visual quality. + +- Fix hierarchy (what is primary vs secondary vs tertiary?) +- Fix spacing (grouping clarity; rhythm; alignment) +- Fix typography (scale, weights, line height, line length) +- Fix colour (contrast, palette consistency, accent usage) +- Fix depth (shadows/borders; focus on meaning) +- Improve empty states and microcopy + +Return: +1) a list of concrete changes +2) updated tokens (if needed) +3) before/after descriptions of the most important screens. +``` + +## THE EXACT PROMPT — Usability “glance test” + +``` +Pretend you have 10 seconds to look at this page. + +Answer: +- What is this page? +- Who is it for? +- What are the top 3 things I can do here? +- What is the primary action? +- Where is the navigation? + +Then list everything that created a question mark, and propose fixes. +``` + +## THE EXACT PROMPT — Component spec (single component) + +``` +Write a build-ready spec for this component. + +Include: +- Purpose + when to use +- Anatomy (parts) +- Variants +- States: default/hover/focus/active/disabled/loading/error/success/empty +- Behaviour rules (keyboard + mouse + touch) +- Spacing + typography + colour tokens used +- Accessibility notes (ARIA if needed) +- Edge cases (long text, missing data, localisation) + +Keep it concise but unambiguous. +``` + +## THE EXACT PROMPT — Accessibility pass + +``` +Review this design for accessibility. + +Check: +- text contrast (normal and large text) +- keyboard navigation and focus visibility +- semantic element choices (button vs link vs div) +- form labelling and error announcement +- motion and reduced-motion behaviour + +Return: +1) issues grouped by severity +2) concrete fixes (design + implementation) +3) any token changes needed (colours, focus styles) +``` + +## THE EXACT PROMPT — Responsive pass + +``` +Define responsive behaviour for this page/component. + +For each breakpoint (small phone, large phone, tablet, desktop): +- layout (stack/columns) +- what becomes primary vs secondary +- how text wraps/truncates +- how tables, toolbars, and secondary actions adapt + +Then list edge cases (long text, empty, error) and how they render. +``` diff --git a/.agents/skills/designing-beautiful-websites/references/ACCESSIBILITY.md b/.agents/skills/designing-beautiful-websites/references/ACCESSIBILITY.md new file mode 100644 index 0000000..cc7c99b --- /dev/null +++ b/.agents/skills/designing-beautiful-websites/references/ACCESSIBILITY.md @@ -0,0 +1,66 @@ +# Accessibility Essentials for Web UX/UI + +> Use this when designing, auditing, or specifying UI. Accessibility is a quality baseline, not a nice-to-have. + +## Table of contents +- [Non-negotiables](#non-negotiables) +- [Colour and contrast](#colour-and-contrast) +- [Keyboard and focus](#keyboard-and-focus) +- [Semantic HTML](#semantic-html) +- [Forms and errors](#forms-and-errors) +- [Motion and animation](#motion-and-animation) +- [Optional: contrast check script](#optional-contrast-check-script) + +## Non-negotiables +- All interactive elements are reachable via keyboard. +- Focus is visible and logical. +- Text has sufficient contrast. +- Controls have programmatic labels. +- Errors are understandable and recoverable. + +## Colour and contrast +Targets: +- normal text: ≥ 4.5:1 +- large text: ≥ 3:1 + +Rules: +- don’t rely on colour alone to communicate state (also use icons, text, or patterns). +- if white text forces a coloured background to become too dark and visually dominant, use dark text on a light tint. + +## Keyboard and focus +Rules: +- focus order follows visual order. +- focus styles are obvious (don’t remove outlines without replacing them). +- modals trap focus and restore it on close. + +## Semantic HTML +Prefer native elements: +- ` + + + +
+ + {t.filterByCategory} + +
+ + {t.all} + + + {categories.map((cat) => { + const catUrl = search + ? `/?search=${encodeURIComponent(search)}&category=${encodeURIComponent(cat.name)}&limit=${limit}&page=1` + : `/?category=${encodeURIComponent(cat.name)}&limit=${limit}&page=1`; + return ( + + {cat.name} + + ); + })} +
+
+ + {(category !== "all" || !!search) && ( +
+ + {t.currentFilter}{" "} + + {category !== "all" ? category : ""} {search ? `「${search}」` : ""} + + + + + {t.clearFilter} + +
+ )} + + + + + ); +} diff --git a/pkgs/rssbook/src/books/magazine/home/components/controls/Status.tsx b/pkgs/rssbook/src/books/magazine/home/components/controls/Status.tsx new file mode 100644 index 0000000..8af27a4 --- /dev/null +++ b/pkgs/rssbook/src/books/magazine/home/components/controls/Status.tsx @@ -0,0 +1,80 @@ +import type { FilterInfo, PaginationInfo } from "@/types"; +import type { Translations } from "../../i18n"; + +interface StatusProps { + pagination: PaginationInfo; + itemCount: number; + filter: FilterInfo; + t: Translations; +} + +export function Status({ pagination, itemCount, filter, t }: StatusProps) { + const { page, totalPages, total, limit, hasPrev, hasNext } = pagination; + const { search, category } = filter; + + const buildPageUrl = (targetPage: number): string => { + const parts = [`page=${targetPage}`, `limit=${limit}`]; + if (search) parts.push(`search=${encodeURIComponent(search)}`); + if (category && category !== "all") parts.push(`category=${encodeURIComponent(category)}`); + return `/?${parts.join("&")}`; + }; + + const prevUrl = hasPrev ? buildPageUrl(page - 1) : undefined; + const nextUrl = hasNext ? buildPageUrl(page + 1) : undefined; + const safeItemCount = itemCount; + const safeArticlesOfTotal = t.articlesOfTotal.replace("{total}", String(total)); + const safePage = page; + const safeTotalPages = totalPages; + + return ( +
+
+
+ + {t.loaded} + + + {safeItemCount} {safeArticlesOfTotal} + +
+ +
+ {t.page} + {safePage} + {!!t.total && {t.total}} + / + {t.of} + {safeTotalPages} + {!!t.pages && {t.pages}} +
+ + +
+
+ ); +} diff --git a/pkgs/rssbook/src/books/magazine/home/components/layout/Background.tsx b/pkgs/rssbook/src/books/magazine/home/components/layout/Background.tsx new file mode 100644 index 0000000..382187f --- /dev/null +++ b/pkgs/rssbook/src/books/magazine/home/components/layout/Background.tsx @@ -0,0 +1,13 @@ +export function Background() { + return ( +
+
+
+
+
+
+ ); +} diff --git a/pkgs/rssbook/src/books/magazine/home/components/layout/Footer.tsx b/pkgs/rssbook/src/books/magazine/home/components/layout/Footer.tsx new file mode 100644 index 0000000..2283880 --- /dev/null +++ b/pkgs/rssbook/src/books/magazine/home/components/layout/Footer.tsx @@ -0,0 +1,79 @@ +import { formatHTML } from "@/utils"; +import type { Translations } from "../../i18n"; + +interface FooterProps { + title: string; + description?: string; + rss?: string; + atom?: string; + json?: string; + t: Translations; +} + +export function Footer({ title, description, rss, atom, json, t }: FooterProps) { + const currentYear = new Date().getFullYear(); + const safeDescription = description ? formatHTML(description) : ""; + + return ( +
+
+
+
+

+ {title} +

+ {!!safeDescription && ( +

+ {safeDescription} +

+ )} +
+ +
+

+ {t.quickNavigation} +

+ +
+ +
+

+ {t.proudlyMadeWith} +

+

+ © {currentYear} {title || "RSSBook"}.{" "} + {t.allRightsReserved} +

+
+
+ +
+

+ Magazine Theme — RSSBook +

+
+
+
+ ); +} diff --git a/pkgs/rssbook/src/books/magazine/home/components/layout/Nav.tsx b/pkgs/rssbook/src/books/magazine/home/components/layout/Nav.tsx new file mode 100644 index 0000000..511ee32 --- /dev/null +++ b/pkgs/rssbook/src/books/magazine/home/components/layout/Nav.tsx @@ -0,0 +1,75 @@ +import { formatHTML } from "@/utils"; +import type { Translations } from "../../i18n"; + +interface NavProps { + title: string; + description?: string; + t: Translations; +} + +export function Nav({ title, description, t }: NavProps) { + const safeDescription = description ? formatHTML(description) : ""; + + return ( + + ); +} diff --git a/pkgs/rssbook/src/books/magazine/home/i18n.ts b/pkgs/rssbook/src/books/magazine/home/i18n.ts new file mode 100644 index 0000000..a82a441 --- /dev/null +++ b/pkgs/rssbook/src/books/magazine/home/i18n.ts @@ -0,0 +1,136 @@ +import type { Language } from "@/types"; + +export type Translations = { + search: string; + home: string; + toggleDarkMode: string; + searchArticles: string; + enterKeywords: string; + filterByCategory: string; + all: string; + currentFilter: string; + clearFilter: string; + closeFilterBar: string; + noContent: string; + pageContentDisplayed: string; + unknownDate: string; + mediaAttachment: string; + duration: string; + play: string; + loaded: string; + articlesOfTotal: string; + page: string; + of: string; + total: string; + pages: string; + previousPage: string; + nextPage: string; + quickNavigation: string; + allRightsReserved: string; + proudlyMadeWith: string; + searchResults: string; + searchResultsFor: string; + filteredByCategory: string; + clearAll: string; + featured: string; + readMore: string; +}; + +const zhCN: Translations = { + all: "全部", + allRightsReserved: "保留所有权利.", + articlesOfTotal: "篇文章 / 共 {total} 篇", + clearAll: "清除全部", + clearFilter: "清除筛选", + closeFilterBar: "关闭筛选栏", + currentFilter: "当前筛选:", + duration: "时长:", + enterKeywords: "输入关键词...", + featured: "精选", + filterByCategory: "按分类筛选", + filteredByCategory: "分类筛选:", + home: "首页", + loaded: "已加载", + mediaAttachment: "媒体附件", + nextPage: "下一页", + noContent: "暂无内容", + of: "共", + page: "第", + pageContentDisplayed: "当前页内容已展示完毕", + pages: "页", + play: "播放", + previousPage: "上一页", + proudlyMadeWith: "自豪地采用 RSSBook 制作。", + quickNavigation: "快速导航", + readMore: "阅读更多", + search: "搜索", + searchArticles: "搜索文章", + searchResults: "搜索结果", + searchResultsFor: "搜索:", + toggleDarkMode: "切换暗色模式", + total: "页", + unknownDate: "未知日期", +}; + +const en: Translations = { + all: "All", + allRightsReserved: "All rights reserved.", + articlesOfTotal: "articles / {total} total", + clearAll: "Clear all", + clearFilter: "Clear filter", + closeFilterBar: "Close filter bar", + currentFilter: "Current filter:", + duration: "Duration:", + enterKeywords: "Enter keywords...", + featured: "Featured", + filterByCategory: "Filter by category", + filteredByCategory: "Category:", + home: "Home", + loaded: "Loaded", + mediaAttachment: "Media attachment", + nextPage: "Next page", + noContent: "No content", + of: "of", + page: "Page", + pageContentDisplayed: "Current page content fully displayed", + pages: "", + play: "Play", + previousPage: "Previous page", + proudlyMadeWith: "Proudly made with RSSBook.", + quickNavigation: "Quick Navigation", + readMore: "Read more", + search: "Search", + searchArticles: "Search articles", + searchResults: "Search results", + searchResultsFor: "Search:", + toggleDarkMode: "Toggle dark mode", + total: "", + unknownDate: "Unknown date", +}; + +const translations: Record = { + en: en, + "en-GB": en, + "en-US": en, + zh: zhCN, + "zh-CN": zhCN, +}; + +export function getTranslations(lang?: Language | string): Translations { + if (!lang) { + return zhCN; + } + + const normalizedLang = lang.toLowerCase(); + + if (translations[normalizedLang]) { + return translations[normalizedLang]; + } + + const primary = normalizedLang.split("-")[0]; + if (translations[primary]) { + return translations[primary]; + } + + return zhCN; +} diff --git a/pkgs/rssbook/src/books/magazine/home/index.tsx b/pkgs/rssbook/src/books/magazine/home/index.tsx new file mode 100644 index 0000000..bc488fb --- /dev/null +++ b/pkgs/rssbook/src/books/magazine/home/index.tsx @@ -0,0 +1,162 @@ +import type { ThemeProps } from "@/types"; +import { Featured } from "./components/content/Featured"; +import { Grid } from "./components/content/Grid"; +import { FilterBar } from "./components/controls/FilterBar"; +import { Status } from "./components/controls/Status"; +import { Background } from "./components/layout/Background"; +import { Footer } from "./components/layout/Footer"; +import { Nav } from "./components/layout/Nav"; +import { getTranslations } from "./i18n"; + +export const magazineTheme = { + render: ({ + meta: { + description, + favicon, + keywords, + lang, + title, + appleTouchIcon, + atomFeed, + jsonFeed, + rssFeed, + twitter, + og, + extra, + }, + categories, + items, + pagination, + filter: filterInfo, + }: ThemeProps) => { + const t = getTranslations(lang); + const featuredItem = items[0]; + const gridItems = items.slice(1); + const safeItemCount = items.length; + const safeArticlesOfTotal = t.articlesOfTotal.replace("{total}", String(pagination.total)); + + return ( + + + {title || "RSSBook"} + + + + + {!!description && } + {!!keywords && ( + + )} + + + + {!!appleTouchIcon && } + + {!!atomFeed && } + {!!rssFeed && } + {!!jsonFeed && } + + {!!og?.title && } + {!!og?.description && } + {!!og?.type && } + {!!og?.url && } + {!!og?.image && } + {!!og?.siteName && } + + {!!twitter?.card && } + {!!twitter?.title && } + {!!twitter?.description && ( + + )} + {!!twitter?.image && } + {!!twitter?.site && } + + {extra?.map((prop) => { + return ; + })} + + + + + + + + + + + + + + + + + + + +