oneie
Version:
Build apps, websites, and AI agents in English. Zero-interaction setup for AI agents (Claude Code, Cursor, Windsurf). Download to your computer, run in the cloud, deploy to the edge. Open source and free forever.
862 lines (686 loc) • 18.5 kB
Markdown
title: Improve
dimension: things
category: features
tags: ai
related_dimensions: connections, events
scope: global
created: 2025-11-03
updated: 2025-11-03
version: 1.0.0
ai_context: |
This document is part of the things dimension in the features category.
Location: one/things/features/improve.md
Purpose: Documents astro + shadcn/ui template - comprehensive improvement plan
Related dimensions: connections, events
For AI agents: Read this to understand improve.
# Astro + shadcn/ui Template - Comprehensive Improvement Plan
## Executive Summary
This template represents an excellent foundation with modern technologies (Astro 5, React 19, Tailwind CSS v4, shadcn/ui). However, there are significant opportunities to enhance performance, SEO, accessibility, developer experience, and feature richness. This document provides a prioritized roadmap for improvements.
## 🔴 Critical Priority (Do First)
### 1. Performance Optimization
**Current Issues:**
- Using plain `<img>` tags instead of Astro's optimized `<Image>` component
- Excessive `client:load` directives on static components (Badge, Calendar icons)
- No lazy loading strategy
- Missing font optimization
**Improvements:**
```astro
<!-- BEFORE: src/pages/blog/index.astro:104 -->
<img
src={entry.data.picture}
alt={entry.data.title}
class="w-full h-full object-cover"
/>
<!-- AFTER: Use Astro's Image component -->
import {Image} from 'astro:assets';
<Image
src={entry.data.picture}
alt={entry.data.title}
width={800}
height={450}
loading="lazy"
class="w-full h-full object-cover"
/>
```
```astro
<!-- BEFORE: Unnecessary hydration -->
<Badge variant="secondary" client:load>{entry.data.type}</Badge>
<Calendar className="w-4 h-4 mr-1" client:load />
<!-- AFTER: Static rendering -->
<Badge variant="secondary">{entry.data.type}</Badge>
<Calendar className="w-4 h-4 mr-1" />
```
**Font Optimization:**
```astro
<!-- Add to Layout.astro head -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link
rel="preload"
as="font"
href="/fonts/font-name.woff2"
type="font/woff2"
crossorigin
/>
```
**Expected Impact:**
- 30-50% reduction in JavaScript bundle size
- 2-3x faster image loading
- Improved Lighthouse performance score from ~85 to 95+
### 2. SEO Enhancement
**Missing Critical SEO Elements:**
```astro
<!-- Add to Layout.astro:21-28 -->
<meta property="og:title" content={title} />
<meta property="og:description" content={description} />
<meta property="og:type" content="website" />
<meta property="og:url" content={Astro.url} />
<meta property="og:image" content={new URL('/og.jpg', Astro.url)} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<link rel="canonical" href={Astro.url.pathname} />
```
**Generate Sitemap:**
```typescript
// astro.config.mjs
import sitemap from "@astrojs/sitemap";
export default defineConfig({
site: "https://your-domain.com",
integrations: [react(), sitemap()],
});
```
**Add RSS Feed:**
```typescript
// src/pages/rss.xml.ts
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";
export async function GET(context) {
const blog = await getCollection("blog");
return rss({
title: "Your Blog",
description: "A beautiful blog",
site: context.site,
items: blog.map((post) => ({
title: post.data.title,
pubDate: post.data.date,
description: post.data.description,
link: `/blog/${post.slug}/`,
})),
});
}
```
**Add Structured Data:**
```astro
<!-- src/pages/blog/[...slug].astro -->
<script
type="application/ld+json"
set:html={JSON.stringify({
'@context': 'https://schema.org',
'@type': 'BlogPosting',
headline: entry.data.title,
datePublished: entry.data.date,
description: entry.data.description,
author: { '@type': 'Person', name: 'Your Name' },
})}
/>
```
### 3. Accessibility Improvements
**Critical Issues:**
- No skip-to-content link
- Missing ARIA labels
- Keyboard navigation incomplete
- Missing focus indicators
**Fixes:**
```astro
<!-- Add skip link to Layout.astro:32 -->
<a
href="#main-content"
class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 focus:z-50 focus:px-4 focus:py-2 focus:bg-primary focus:text-primary-foreground"
>
Skip to main content
</a>
<!-- Add id to main -->
<main id="main-content" class="flex-1 p-6"></main>
```
```tsx
// src/components/Sidebar.tsx - Add ARIA labels
<nav aria-label="Main navigation" className="flex-1 pl-1">
{navigation.map((item) => (
<a
aria-label={item.title}
aria-current={activePath === item.path ? 'page' : undefined}
// ... rest of props
>
```
**Focus Indicators (add to global.css):**
```css
/* Enhanced focus styles */
*:focus-visible {
outline: 2px solid hsl(var(--color-ring));
outline-offset: 2px;
}
/* Skip link styles */
.sr-only:not(:focus):not(:active) {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
```
## 🟡 High Priority
### 4. Content Collections Enhancement
**Improve Blog Schema:**
```typescript
// src/content/config.ts
const BlogSchema = z.object({
title: z.string(),
description: z.string(),
date: z.date(),
draft: z.boolean().default(false),
image: z.string().optional(), // Remove duplicate picture/image
author: z.string().default("Default Author"),
tags: z.array(z.string()).default([]),
category: z.enum(["tutorial", "news", "guide", "review"]).default("tutorial"),
readingTime: z.number().optional(), // Auto-calculate or manual
featured: z.boolean().default(false),
relatedPosts: z.array(z.string()).optional(), // Slugs of related posts
});
```
**Add Reading Time Calculation:**
```typescript
// src/lib/reading-time.ts
export function calculateReadingTime(content: string): number {
const wordsPerMinute = 200;
const wordCount = content.trim().split(/\s+/).length;
return Math.ceil(wordCount / wordsPerMinute);
}
```
### 5. Blog Features Enhancement
**Add Pagination:**
```astro
// src/pages/blog/[page].astro
export async function getStaticPaths({ paginate }) {
const posts = await getCollection('blog');
return paginate(posts, { pageSize: 12 });
}
const { page } = Astro.props;
<!-- Pagination UI -->
<div class="flex gap-2 justify-center mt-8">
{page.url.prev && <a href={page.url.prev}>Previous</a>}
<span>Page {page.currentPage} of {page.lastPage}</span>
{page.url.next && <a href={page.url.next}>Next</a>}
</div>
```
**Add Tag Filtering:**
```astro
// src/pages/blog/tag/[tag].astro
export async function getStaticPaths() {
const posts = await getCollection('blog');
const tags = [...new Set(posts.flatMap((post) => post.data.tags))];
return tags.map((tag) => ({
params: { tag },
props: {
posts: posts.filter((post) => post.data.tags.includes(tag)),
tag,
},
}));
}
```
**Add Search Functionality:**
```typescript
// src/components/BlogSearch.tsx
import { useState, useMemo } from 'react';
import { Input } from '@/components/ui/input';
export function BlogSearch({ posts }) {
const [query, setQuery] = useState('');
const filtered = useMemo(() =>
posts.filter(post =>
post.data.title.toLowerCase().includes(query.toLowerCase()) ||
post.data.description.toLowerCase().includes(query.toLowerCase())
), [query, posts]
);
return (
<div>
<Input
type="search"
placeholder="Search posts..."
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
{/* Render filtered posts */}
</div>
);
}
```
**Add Table of Contents:**
```typescript
// src/components/TableOfContents.tsx
interface TOCProps {
headings: { depth: number; slug: string; text: string; }[];
}
export function TableOfContents({ headings }: TOCProps) {
return (
<nav className="sticky top-4">
<h3 className="font-semibold mb-2">On this page</h3>
<ul className="space-y-1">
{headings.map(h => (
<li key={h.slug} style={{ paddingLeft: `${(h.depth - 2) * 1}rem` }}>
<a href={`#${h.slug}`} className="text-sm hover:text-primary">
{h.text}
</a>
</li>
))}
</ul>
</nav>
);
}
```
### 6. Error Handling & Edge Cases
**Add 404 Page:**
```astro
// src/pages/404.astro
import Layout from '@/layouts/Layout.astro';
import { Button } from '@/components/ui/button';
<Layout title="404 - Page Not Found">
<div
class="flex flex-col items-center justify-center min-h-[60vh] text-center"
>
<h1 class="text-6xl font-bold mb-4">404</h1>
<p class="text-xl text-muted-foreground mb-6">
Oops! The page you're looking for doesn't exist.
</p>
<Button client:load>
<a href="/">Go Home</a>
</Button>
</div>
</Layout>
```
**Add Error Boundary:**
```tsx
// src/components/ErrorBoundary.tsx
import { Component, type ReactNode } from "react";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
error?: Error;
}
export class ErrorBoundary extends Component<Props, State> {
state: State = { hasError: false };
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
render() {
if (this.state.hasError) {
return (
<Alert variant="destructive">
<AlertTitle>Something went wrong</AlertTitle>
<AlertDescription>
{this.state.error?.message || "An unexpected error occurred"}
</AlertDescription>
</Alert>
);
}
return this.props.children;
}
}
```
## 🟢 Medium Priority
### 7. Developer Experience
**Add ESLint & Prettier:**
```json
// .eslintrc.json
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:astro/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"overrides": [
{
"files": ["*.astro"],
"parser": "astro-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"extraFileExtensions": [".astro"]
}
}
]
}
```
```json
// .prettierrc
{
"semi": true,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"plugins": ["prettier-plugin-astro"],
"overrides": [
{
"files": "*.astro",
"options": { "parser": "astro" }
}
]
}
```
**Add Git Hooks:**
```bash
pnpm add -D husky lint-staged
npx husky init
echo "pnpm lint-staged" > .husky/pre-commit
```
```json
// package.json
{
"lint-staged": {
"*.{ts,tsx,astro}": ["eslint --fix", "prettier --write"],
"*.{json,md}": ["prettier --write"]
}
}
```
**Add Testing Setup:**
```typescript
// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [react()],
test: {
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
},
});
```
**VS Code Settings:**
```json
// .vscode/settings.json
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"[astro]": {
"editor.defaultFormatter": "astro-build.astro-vscode"
},
"tailwindCSS.experimental.classRegex": [
["cn\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
]
}
```
### 8. Mobile Optimization
**Improve Mobile Sidebar:**
```tsx
// src/components/Sidebar.tsx - Add mobile version
const MobileSidebar = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button
onClick={() => setIsOpen(true)}
className="fixed top-4 left-4 z-50 md:hidden"
aria-label="Open menu"
>
<Menu className="h-6 w-6" />
</button>
<Sheet open={isOpen} onOpenChange={setIsOpen}>
<SheetContent side="left" className="w-64">
<nav className="space-y-2">{/* Navigation items */}</nav>
</SheetContent>
</Sheet>
</>
);
};
```
**Touch Gestures:**
```typescript
// Add swipe-to-close for mobile sidebar
import { useSwipeable } from "react-swipeable";
const handlers = useSwipeable({
onSwipedLeft: () => setIsOpen(false),
trackMouse: false,
});
```
### 9. Architecture Improvements
**Environment Variable Validation:**
```typescript
// src/env.ts
import { z } from "zod";
const envSchema = z.object({
PUBLIC_SITE_URL: z.string().url(),
PUBLIC_ANALYTICS_ID: z.string().optional(),
GITHUB_TOKEN: z.string().optional(),
});
export const env = envSchema.parse(import.meta.env);
```
**Remove Unused Path Alias:**
```json
// tsconfig.json - Remove this line:
"@1/*": ["src/1/*"], // REMOVE
```
**Make Navigation Configurable:**
```typescript
// src/config/navigation.ts
export const siteConfig = {
name: "Your Site",
navigation: [
{ title: "Home", path: "/", icon: "Home" },
{ title: "Blog", path: "/blog", icon: "Book" },
// ... more items
],
social: {
github: "https://github.com/yourusername",
twitter: "https://twitter.com/yourusername",
},
};
```
## 🔵 Low Priority (Nice to Have)
### 10. Advanced Features
**Dark Mode Transition Animation:**
```css
/* global.css */
::view-transition-old(root),
::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
.dark::view-transition-old(root) {
z-index: 1;
}
.dark::view-transition-new(root) {
z-index: 999;
}
```
**Social Sharing:**
```tsx
// src/components/ShareButtons.tsx
export function ShareButtons({ title, url }) {
const shareData = {
title,
url: typeof window !== "undefined" ? window.location.href : url,
};
return (
<div className="flex gap-2">
<button onClick={() => navigator.share?.(shareData)}>Share</button>
<a href={`https://twitter.com/intent/tweet?text=${title}&url=${url}`}>
Twitter
</a>
<a href={`https://www.facebook.com/sharer/sharer.php?u=${url}`}>
Facebook
</a>
</div>
);
}
```
**Analytics Integration:**
```astro
const { analyticsId } = Astro.props;
<!-- src/components/Analytics.astro -->{
analyticsId && (
<script is:inline define:vars={{ analyticsId }}>
// Google Analytics or Plausible code
</script>
)
}
```
**View Transitions:**
```typescript
// astro.config.mjs
export default defineConfig({
experimental: {
viewTransitions: true,
},
});
```
```astro
<!-- Layout.astro -->import {ViewTransitions} from 'astro:transitions';
<head>
<ViewTransitions />
</head>
```
### 11. Security Enhancements
**Content Security Policy:**
```typescript
// src/middleware.ts
export const onRequest = async (context, next) => {
const response = await next();
response.headers.set(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';",
);
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
return response;
};
```
### 12. Documentation Improvements
**Component Documentation:**
````tsx
/**
* Sidebar component with expandable navigation
*
* @example
* ```tsx
* <Sidebar client:load />
* ```
*
* @remarks
* - Expands on hover (desktop)
* - Sheet drawer on mobile
* - Auto-detects active route
*/
export function Sidebar() { ... }
````
**README Enhancement:**
```markdown
## 🚀 Quick Start
\`\`\`bash
# Clone the repository
git clone https://github.com/your-repo/astro-shadcn
# Install dependencies
pnpm install
# Start dev server
pnpm dev
# Build for production
pnpm build
\`\`\`
## 📝 Adding Blog Posts
Create a new `.md` file in `src/content/blog/`:
## \`\`\`markdown
title: "My Post"
description: "Description"
date: 2025-09-30
tags: ["astro", "react"]
Your content here...
\`\`\`
```
## Implementation Roadmap
### Week 1: Critical Fixes
- [ ] Replace `<img>` with `<Image>` components
- [ ] Remove unnecessary `client:load` directives
- [ ] Add SEO meta tags (OG, Twitter Cards)
- [ ] Implement skip-to-content link
- [ ] Add ARIA labels
### Week 2: Content Enhancement
- [ ] Improve blog schema (tags, categories, reading time)
- [ ] Add pagination to blog
- [ ] Implement tag filtering
- [ ] Create 404 page
- [ ] Add error boundary
### Week 3: Developer Experience
- [ ] Setup ESLint + Prettier
- [ ] Add git hooks (husky)
- [ ] Configure VS Code settings
- [ ] Add basic testing setup
### Week 4: Features & Polish
- [ ] Add search functionality
- [ ] Implement table of contents
- [ ] Improve mobile sidebar
- [ ] Add social sharing buttons
- [ ] Generate sitemap and RSS feed
### Week 5: Advanced Features
- [ ] Add analytics integration
- [ ] Implement view transitions
- [ ] Add security headers
- [ ] Performance audit and optimization
- [ ] Documentation improvements
## Metrics & Goals
### Performance Targets
- Lighthouse Performance: 95+ (currently ~85)
- First Contentful Paint: < 1.2s
- Time to Interactive: < 2.5s
- Total Bundle Size: < 200KB (currently ~350KB)
### SEO Targets
- Lighthouse SEO: 100 (currently ~85)
- All pages have unique meta descriptions
- Sitemap covering all pages
- Valid structured data on all content pages
### Accessibility Targets
- Lighthouse Accessibility: 100 (currently ~90)
- WCAG 2.1 AA compliance
- Keyboard navigation for all interactive elements
- Screen reader tested
## Conclusion
This template has an excellent foundation, but implementing these improvements will transform it into a production-ready, enterprise-grade solution. Prioritize the critical and high-priority items first, as they provide the most value for effort invested.
The key areas requiring immediate attention are:
1. **Performance** - Remove unnecessary hydration, optimize images
2. **SEO** - Add meta tags, sitemap, RSS feed
3. **Accessibility** - ARIA labels, keyboard navigation, focus management
4. **Content Features** - Pagination, tags, search
By following this roadmap, you'll have a template that not only looks beautiful but performs exceptionally well in real-world production scenarios.