Token导航 LogoToken导航TokenDH.com
研究检索操作浏览器github未标认证来源可访问许可证需确认审计提醒

publishing-astro-websites发布 astro 网站

Agent Skill

publishing-astro-websites 用于查找、检索和筛选相关信息,适合在 Codex、Claude、Cursor、Gemini CLI 中需要根据关键词、任务场景或来源线索快速定位候选结果时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

570

周安装

24

GitHub Stars

11

下载量

331
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:publishing-astro-websites(发布 astro 网站)
来源仓库:https://github.com/spillwavesolutions/publishing-astro-websites-agentic-skill
仓库路径:skills/publishing-astro-websites
安装命令:
npx skills add https://github.com/spillwavesolutions/publishing-astro-websites-agentic-skill --skill publishing-astro-websites
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/spillwavesolutions/publishing-astro-websites-agentic-skill --skill publishing-astro-websites

简介

用于查找、检索和筛选相关信息。

  • 适合在 Codex、Claude、Cursor、Gemini CLI 中根据关键词、任务场景或来源线索快速定位候选结果。
  • 通过 npx skills add 命令从指定 GitHub 仓库安装使用。
  • 建议确认权限范围和维护状态,注意可能触发联网、命令执行或文件读写操作。
  • publishing-astro-websites 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

Publishing Astro Websites

Build fast, content-driven static websites with Astro's zero-runtime SSG approach, partial hydration, and extensive Markdown support.

Contents

Quick Start

# Create new project (use Blog template for Markdown sites)
npm create astro@latest

# Development
npm run dev          # Local server at http://localhost:4321
npm run build        # Generate static files in dist/
npm run preview      # Preview production build

When Not to Use

This skill focuses on static site generation (SSG). Consider other approaches for:

  • Real-time data applications - Use SSR mode with database connections
  • User authentication flows - Requires server-side session handling
  • E-commerce with dynamic inventory - Use hybrid mode or full SSR
  • Single-page applications (SPAs) - Consider React/Vue frameworks directly

For hybrid SSG+SSR patterns, see Astro's adapter documentation.

Project Structure

src/
  components/     # Astro, React, Vue, Svelte components
  content/        # Content Collections (Markdown/MDX)
    config.ts     # Collection schemas
    docs/         # Example collection
  layouts/        # Page wrappers with slots
  pages/          # File-based routing
public/           # Static assets (images, fonts, favicons)
astro.config.mjs  # Framework configuration

SSG vs SSR vs Hybrid

ModeWhen Pages RenderUse Case
SSG (default)Build timeBlogs, docs, marketing sites
SSREach requestDynamic data, personalization
HybridMix of bothStatic pages + dynamic endpoints

For pure static sites, use default output: 'static' - no adapter needed.

Content Collections

Legacy Pattern (Astro 4.x)

Define schemas in src/content/config.ts:

import { defineCollection, z } from "astro:content";

export const collections = {
  docs: defineCollection({
    schema: z.object({
      title: z.string(),
      description: z.string().optional(),
      tags: z.array(z.string()).optional(),
      order: z.number().optional(),
      draft: z.boolean().default(false)
    })
  })
};

Content Layer API (Astro 5.0+)

New pattern with glob() loader - up to 75% faster builds for large sites:

// src/content.config.ts (note: different filename)
import { defineCollection } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';

const blog = defineCollection({
  loader: glob({ pattern: '**/*.md', base: './src/data/blog' }),
  schema: ({ image }) => z.object({
    title: z.string(),
    pubDate: z.coerce.date(),
    draft: z.boolean().default(false),
    cover: image(),  // Validates image exists
    author: reference('authors'),  // Cross-collection reference
  })
});

export const collections = { blog };

Advanced Schema Patterns

schema: ({ image }) => z.object({
  cover: image(),                    // Validates image in src/
  category: z.enum(['tech', 'news']),
  author: reference('authors'),      // Cross-collection ref
  relatedPosts: z.array(reference('blog')).optional(),
})

Custom Loaders (Remote Content)

Fetch content from external APIs (GitHub releases, CMS, etc.):

// src/loaders/github-releases.ts
import type { Loader } from 'astro/loaders';

export function githubReleasesLoader(repo: string): Loader {
  return {
    name: 'github-releases',
    load: async ({ store, logger }) => {
      logger.info(`Fetching releases for ${repo}`);
      const response = await fetch(`https://api.github.com/repos/${repo}/releases`);
      const releases = await response.json();

      for (const release of releases) {
        store.set({
          id: release.tag_name,
          data: {
            version: release.tag_name,
            published_at: release.published_at,
            body: release.body  // Markdown release notes
          }
        });
      }
    }
  };
}

Register in content.config.ts:

import { githubReleasesLoader } from './loaders/github-releases';

const releases = defineCollection({
  loader: githubReleasesLoader('owner/repo'),
  schema: z.object({
    version: z.string(),
    published_at: z.string(),
    body: z.string(),
  })
});

Query and render collections:

---
import { getCollection } from "astro:content";

export async function getStaticPaths() {
  const docs = await getCollection("docs");
  return docs.map(doc => ({
    params: { slug: doc.slug },
    props: { doc }
  }));
}

const { doc } = Astro.props;
const { Content } = await doc.render();
---

<article>
  <h1>{doc.data.title}</h1>
  <Content />
</article>

Syntax Highlighting

Basic Shiki Configuration

import { defineConfig } from "astro/config";

export default defineConfig({
  markdown: {
    shikiConfig: {
      theme: "github-dark",
      wrap: true
    }
  }
});

Dual Light/Dark Theme

shikiConfig: {
  themes: {
    light: 'github-light',
    dark: 'github-dark',
  },
}

Add CSS to switch themes:

@media (prefers-color-scheme: dark) {
  .astro-code, .astro-code span {
    color: var(--shiki-dark) !important;
    background-color: var(--shiki-dark-bg) !important;
  }
}

Line Highlighting and Transformers

const a = 1; const b = 2; // highlighted const c = 3; console.log(a + b + c); // highlighted

Shiki Transformers (Astro 4.14+):

import { transformerNotationFocus, transformerNotationDiff } from '@shikijs/transformers';

shikiConfig: {
  transformers: [transformerNotationFocus(), transformerNotationDiff()],
}

Use notation comments in code:

  • // [!code focus] - Focus this line
  • // [!code ++] - Mark as addition (green)
  • // [!code --] - Mark as deletion (red)

Expressive Code (Recommended for Docs)

Rich code blocks with copy buttons, filenames, diff highlighting:

npm install astro-expressive-code
import expressiveCode from 'astro-expressive-code';

export default defineConfig({
  integrations: [expressiveCode()],
});

Features: Copy button, file tabs, line markers, terminal frames, text markers.

Diagram Integration

Mermaid (Recommended)

Install the Astro integration:

npm install astro-mermaid mermaid
// astro.config.mjs
import { defineConfig } from 'astro/config';
import mermaid from 'astro-mermaid';

export default defineConfig({
  integrations: [mermaid({ theme: 'default' })]
});

Use in Markdown:

graph TD; A-->B; B-->C;

Features: Client-side rendering, automatic theme switching, offline capable, no Playwright required.

Alternative (build-time static SVG): Use rehype-mermaid with Playwright for pre-rendered diagrams (npx playwright install --with-deps required).

Dark Mode Theming Strategies:

  1. CSS Variables - Let browser resolve colors at runtime:
// mermaid config
mermaid.initialize({
  theme: 'base',
  themeVariables: {
    primaryColor: 'var(--diagram-primary)',
    lineColor: 'var(--diagram-line)'
  }
});
  1. Picture Element - Generate both themes, swap with media query:
<picture>
  <source srcset="/diagrams/flow-dark.svg" media="(prefers-color-scheme: dark)">
  <img src="/diagrams/flow-light.svg" alt="Flow diagram">
</picture>
  1. Inline SVG - Target SVG classes with CSS (risk: style collisions):
.dark .mermaid-svg .node rect {
  fill: var(--bg-dark);
}

PlantUML

npx astro add plantuml

Use in Markdown:

@startuml Alice -> Bob: Hello Bob --> Alice: Hi! @enduml

Client-Side Search

Pagefind (Recommended for Large Sites)

Zero-config static search that indexes at build time:

npm install pagefind

Add to package.json:

{
  "scripts": {
    "build": "astro build && npx pagefind --site dist",
    "postbuild": "pagefind --site dist"
  }
}

Use in components:

<link href="/pagefind/pagefind-ui.css" rel="stylesheet" />
<script src="/pagefind/pagefind-ui.js" type="text/javascript"></script>

<div id="search"></div>
<script>
  window.addEventListener('DOMContentLoaded', () => {
    new PagefindUI({ element: '#search', showSubResults: true });
  });
</script>

Features: No external service, works offline, automatic indexing, small bundle (~10KB).

Granular Indexing Control:

<!-- Only index main content, not headers/sidebars -->
<main data-pagefind-body>
  <h1 data-pagefind-meta="title">Page Title</h1>
  <p data-pagefind-weight="10">Important intro text</p>

  <!-- Exclude from search snippets -->
  <nav data-pagefind-ignore>
    <a href="/related">Related Posts</a>
  </nav>
</main>
AttributePurpose
data-pagefind-bodyLimit indexing to this element only
data-pagefind-ignoreExclude element from index
data-pagefind-meta="key"Define metadata field
data-pagefind-weight="10"Boost relevance (default: 1)

Pagefind vs Fuse.js

FeaturePagefindFuse.js
ArchitecturePre-built binary chunksRuntime in-memory
BandwidthLow (loads only needed chunks)High (downloads full index)
Scalability10,000+ pages< 500 pages
MultilingualNative stemmingManual config
Use CaseGlobal site searchSmall list filtering

Fuse.js (Lightweight Alternative)

For smaller sites with custom UI needs:

---
import { getCollection } from "astro:content";
const posts = await getCollection('blog');
const searchIndex = JSON.stringify(posts.map(post => ({
  title: post.data.title,
  slug: post.slug,
  body: post.body.slice(0, 500)
})));
---

<input type="search" id="search" placeholder="Search..." />
<ul id="results"></ul>

<script define:vars={{ searchIndex }}>
  import Fuse from 'fuse.js';

  const fuse = new Fuse(JSON.parse(searchIndex), {
    keys: ['title', 'body'],
    threshold: 0.3
  });

  document.getElementById('search').addEventListener('input', (e) => {
    const results = fuse.search(e.target.value);
    const resultsEl = document.getElementById('results');

    // Clear previous results safely
    resultsEl.replaceChildren();

    // Build results using safe DOM methods
    results.forEach(r => {
      const li = document.createElement('li');
      const link = document.createElement('a');
      link.href = `/blog/${r.item.slug}`;
      link.textContent = r.item.title;
      li.appendChild(link);
      resultsEl.appendChild(li);
    });
  });
</script>

For enterprise needs, consider Algolia (hosted search API).

Versioned Documentation

Starlight (Recommended for Docs Sites)

Purpose-built documentation framework on Astro:

npm create astro@latest -- --template starlight

Key features: Built-in search (Pagefind), i18n, sidebar navigation, dark mode, component overrides.

// astro.config.mjs
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';

export default defineConfig({
  integrations: [
    starlight({
      title: 'My Docs',
      sidebar: [
        { label: 'Guides', autogenerate: { directory: 'guides' } },
        { label: 'Reference', autogenerate: { directory: 'reference' } },
      ],
    }),
  ],
});

Multi-Version Docs Pattern

Use folder-based structure:

src/content/docs/
  v1/
    getting-started.md
    api-reference.md
  v2/
    getting-started.md
    api-reference.md
    new-feature.md

For Starlight versioning, use starlight-utils plugin:

npm install starlight-utils
import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
import starlightUtils from 'starlight-utils';

export default defineConfig({
  integrations: [
    starlight({
      plugins: [starlightUtils({ multiSidebar: { switcherStyle: 'dropdown' } })],
      sidebar: [
        { label: 'v2', items: [{ label: 'Guides', autogenerate: { directory: 'v2' } }] },
        { label: 'v1', items: [{ label: 'Guides', autogenerate: { directory: 'v1' } }] },
      ],
    }),
  ],
});

Internationalization (i18n)

Configure in astro.config.mjs:

export default defineConfig({
  i18n: {
    defaultLocale: "en",
    locales: ["en", "fr", "es"],
    routing: {
      prefixDefaultLocale: false
    }
  }
});

Structure content by locale:

src/content/docs/
  en/
    getting-started.md
  fr/
    getting-started.md

Detect locale in components:

---
const locale = Astro.currentLocale || 'en';
---

Fallback for Missing Translations

Show English content with a banner when translations don't exist:

---
// src/pages/[lang]/[...slug].astro
import { getCollection, getEntry } from "astro:content";

const languages = ['en', 'es', 'fr'];
const defaultLang = 'en';

export async function getStaticPaths() {
  const englishDocs = await getCollection('docs', ({ id }) => id.startsWith('en/'));
  const paths = [];

  for (const doc of englishDocs) {
    const slug = doc.id.replace(/^en\//, '');

    for (const lang of languages) {
      const localizedId = `${lang}/${slug}`;
      const localizedDoc = await getEntry('docs', localizedId);

      paths.push({
        params: { lang, slug },
        props: {
          entry: localizedDoc || doc,  // Fallback to English
          isFallback: !localizedDoc
        }
      });
    }
  }
  return paths;
}

const { entry, isFallback } = Astro.props;
const { Content } = await entry.render();
---

{isFallback && (
  <div class="translation-notice">
    This page is not yet available in your language.
  </div>
)}
<Content />

Common Patterns

Paginated Listings

For sites with 50+ posts, split into pages:

---
// src/pages/blog/page/[page].astro
import { getCollection } from "astro:content";

const POSTS_PER_PAGE = 10;

export async function getStaticPaths() {
  const allPosts = await getCollection("blog");
  const totalPages = Math.ceil(allPosts.length / POSTS_PER_PAGE);

  return Array.from({ length: totalPages }, (_, i) => ({
    params: { page: String(i + 1) },
  }));
}

const { page } = Astro.params;
const pageNum = parseInt(page);
const allPosts = await getCollection("blog");
const sortedPosts = allPosts.sort((a, b) =>
  b.data.pubDate.getTime() - a.data.pubDate.getTime()
);

const start = (pageNum - 1) * POSTS_PER_PAGE;
const posts = sortedPosts.slice(start, start + POSTS_PER_PAGE);
const totalPages = Math.ceil(allPosts.length / POSTS_PER_PAGE);
---

{posts.map(post => <article>{post.data.title}</article>)}

<nav>
  {pageNum > 1 && <a href={`/blog/page/${pageNum - 1}`}>← Previous</a>}
  <span>Page {pageNum} of {totalPages}</span>
  {pageNum < totalPages && <a href={`/blog/page/${pageNum + 1}`}>Next →</a>}
</nav>

Tag/Category Archives

Generate a page for each tag:

---
// src/pages/tags/[tag].astro
import { getCollection } from "astro:content";

export async function getStaticPaths() {
  const allPosts = await getCollection("blog");
  const tags = new Set();
  allPosts.forEach(post => post.data.tags?.forEach(tag => tags.add(tag)));

  return Array.from(tags).map(tag => ({
    params: { tag },
    props: { tag },
  }));
}

const { tag } = Astro.props;
const allPosts = await getCollection("blog");
const postsWithTag = allPosts.filter(post => post.data.tags?.includes(tag));
---

<h1>Posts tagged: {tag}</h1>
{postsWithTag.map(post => <a href={`/blog/${post.slug}`}>{post.data.title}</a>)}

RSS Feed

// src/pages/rss.xml.js
import rss from "@astrojs/rss";
import { getCollection } from "astro:content";

export async function GET(context) {
  const blog = await getCollection("blog");
  return rss({
    title: "My Blog",
    description: "A blog about Astro",
    site: context.site,
    items: blog.map(post => ({
      title: post.data.title,
      description: post.data.description,
      pubDate: post.data.pubDate,
      link: `/blog/${post.slug}/`,
    })),
  });
}

Static Forms

For SSG sites, use third-party form handlers:

Formspree (Easiest):

<form action="https://formspree.io/f/YOUR_FORM_ID" method="POST">
  <input type="text" name="name" required />
  <input type="email" name="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

Netlify Forms:

<form name="contact" method="POST" data-netlify="true">
  <input type="hidden" name="form-name" value="contact" />
  <input type="text" name="name" required />
  <button type="submit">Send</button>
</form>

JSON-LD Structured Data

Add rich snippets for SEO:

---
const jsonLd = {
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  headline: entry.data.title,
  description: entry.data.description,
  datePublished: entry.data.pubDate?.toISOString(),
  author: { "@type": "Person", name: entry.data.author },
};
---

<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />

Dark Mode Toggle

<button id="theme-toggle">🌙</button>

<script>
  const toggle = document.getElementById("theme-toggle");
  const html = document.documentElement;

  // Load saved preference or detect system preference
  const savedTheme = localStorage.getItem("theme");
  const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches;

  if (savedTheme === "dark" || (!savedTheme && prefersDark)) {
    html.classList.add("dark");
  }

  toggle?.addEventListener("click", () => {
    html.classList.toggle("dark");
    localStorage.setItem("theme", html.classList.contains("dark") ? "dark" : "light");
  });
</script>

With Tailwind, enable darkMode: "class" in config.

Performance Best Practices

  1. Partial Hydration: Use client:* directives only where needed

- client:load - Hydrate immediately - client:idle - Hydrate when browser is idle - client:visible - Hydrate when in viewport - client:media - Hydrate on media query match - client:only="react" - Skip server render, client-only

  1. Image Optimization: Use Astro's <Image /> component
  2. Keep Static Where Possible: Islands architecture means most content remains static HTML
  3. Asset Fingerprinting: Automatic in production builds
  4. Prefetching: Auto-load links before user clicks
// astro.config.mjs
export default defineConfig({
  prefetch: {
    prefetchAll: true,           // Prefetch all links
    defaultStrategy: 'viewport'  // When links enter viewport
  }
});

Options: 'tap' (on hover/focus), 'viewport' (when visible), 'load' (on page load).

  1. Critical CSS: Inline above-the-fold CSS with astro-critters
npm install astro-critters
import critters from 'astro-critters';

export default defineConfig({
  integrations: [critters()]
});

Extracts critical CSS and inlines it, deferring the rest for faster first paint.

Deployment

Deployment Workflow

  1. Build: Run npm run build and verify dist/ output
  2. Preview: Test with npm run preview at localhost:4321
  3. Configure: Set site, base, and trailingSlash in astro.config.mjs
  4. Platform Setup: Initialize hosting (e.g., firebase init hosting)
  5. Deploy: Push to platform (firebase deploy, vercel, or git push)
  6. Verify: Check live URL, test 404 page, validate assets load

Quick Deploy Commands

# Build for production
npm run build

# Preview before deploy
npm run preview

Platform-Specific

Netlify/Vercel/Cloudflare Pages: Connect Git repository - auto-deploys on push.

GitHub Pages:

// astro.config.mjs
export default defineConfig({
  site: 'https://username.github.io',
  base: '/repo-name'
});

Firebase Hosting:

npm install -g firebase-tools
firebase login
firebase init hosting  # Set public to 'dist'
npm run build
firebase deploy

firebase.json (recommended configuration):

{
  "hosting": {
    "public": "dist",
    "ignore": ["firebase.json", "**/.*"],
    "cleanUrls": true,
    "trailingSlash": false,
    "headers": [
      {
        "source": "/_astro/**",
        "headers": [{"key": "Cache-Control", "value": "public, max-age=31536000, immutable"}]
      }
    ]
  }
}

Align Astro config to prevent redirect loops:

// astro.config.mjs - match Firebase settings
export default defineConfig({
  trailingSlash: 'never',  // Must match Firebase trailingSlash: false
  build: {
    format: 'directory'    // Default - generates /about/index.html
  }
});
Firebase SettingAstro SettingResult
trailingSlash: falsetrailingSlash: 'never'/about (no slash)
trailingSlash: truetrailingSlash: 'always'/about/ (with slash)
MismatchMismatchRedirect loops!

Common Deployment Gotchas

IssueSolution
Trailing slash problemsSet trailingSlash: 'always' or 'never'
Assets not loading on subpathConfigure base in astro.config.mjs
404 not workingCreate custom 404.astro page
Build fails on deployCheck Node version matches local

Pre-Deploy Checklist

Before deploying, verify:

  • npm run build completes without errors
  • npm run preview shows site correctly at localhost:4321
  • All Content Collection schemas validate (astro check)
  • Images use <Image /> component or are in public/
  • SEO metadata present on all pages (title, description, og:*)
  • 404.astro page exists and renders correctly
  • base path configured if deploying to subdirectory
  • Environment variables set on deployment platform
  • trailingSlash setting matches hosting platform expectations
  • RSS feed working (/rss.xml)
  • Sitemap generated (/sitemap-index.xml)
  • Lighthouse score > 90
  • Component tests pass (npm run test)
  • E2E tests pass (npm run test:e2e)
  • Link checker finds no broken links (npx linkinator dist)

Testing & Quality

Static Analysis

# Type checking and validation
npx astro check

# Linting (with ESLint)
npm install -D eslint
npx eslint .

# Preview production build
npm run build && npm run preview

Build-time validation happens automatically with Content Collections - schema errors fail the build.

Component Testing with Vitest

npm install -D vitest @vitest/ui @astrojs/testing
// vitest.config.ts
import { getViteConfig } from 'astro/config';

export default getViteConfig({
  test: {
    include: ['src/**/*.test.ts'],
  },
});
// src/components/Button.test.ts
import { experimental_AstroContainer as AstroContainer } from 'astro/container';
import { expect, test } from 'vitest';
import Button from './Button.astro';

test('Button renders with text', async () => {
  const container = await AstroContainer.create();
  const result = await container.renderToString(Button, {
    props: { text: 'Click me' }
  });
  expect(result).toContain('Click me');
});

E2E Testing with Playwright

npm install -D @playwright/test
npx playwright install
// tests/homepage.spec.ts
import { test, expect } from '@playwright/test';

test('homepage loads correctly', async ({ page }) => {
  await page.goto('/');
  await expect(page).toHaveTitle(/My Site/);
  await expect(page.locator('h1')).toBeVisible();
});

test('navigation works', async ({ page }) => {
  await page.goto('/');
  await page.click('a[href="/about"]');
  await expect(page).toHaveURL(/about/);
});
// package.json
{
  "scripts": {
    "test": "vitest",
    "test:e2e": "playwright test",
    "test:e2e:ui": "playwright test --ui"
  }
}

Link Checking

Validate internal links don't break:

npm install -D linkinator
npx linkinator dist --recurse

Add to CI:

# .github/workflows/links.yml
- run: npm run build
- run: npx linkinator dist --recurse --skip "^(?!http://localhost)"

.astro File Anatomy

---
// Frontmatter: JavaScript/TypeScript runs at build time
import Layout from '../layouts/Layout.astro';
import { getCollection } from 'astro:content';

const { title } = Astro.props;
const posts = await getCollection('blog');
---

<!-- Template: HTML with JSX expressions -->
<Layout title={title}>
  <h1>{title}</h1>
  <ul>
    {posts.map(post => (
      <li><a href={`/blog/${post.slug}`}>{post.data.title}</a></li>
    ))}
  </ul>
</Layout>

<style>
  /* Scoped to this component */
  h1 { color: navy; }
</style>

<script>
  // Client-side JavaScript
  console.log('Runs in browser');
</script>

File-Based Routing

FileRoute
src/pages/index.astro/
src/pages/about.astro/about
src/pages/blog/index.astro/blog
src/pages/blog/[slug].astro/blog/:slug (dynamic)
src/pages/[...path].astroCatch-all

Dynamic routes require getStaticPaths() for SSG:

---
export function getStaticPaths() {
  return [
    { params: { slug: 'post-1' } },
    { params: { slug: 'post-2' } }
  ];
}
---

SEO Essentials

Manual Approach

---
const { title, description, image } = Astro.props;
const canonicalURL = new URL(Astro.url.pathname, Astro.site);
---

<head>
  <title>{title}</title>
  <meta name="description" content={description} />
  <link rel="canonical" href={canonicalURL} />

  <!-- Open Graph -->
  <meta property="og:title" content={title} />
  <meta property="og:description" content={description} />
  <meta property="og:image" content={image} />
  <meta property="og:type" content="website" />

  <!-- Twitter -->
  <meta name="twitter:card" content="summary_large_image" />
</head>

astro-seo (Simplified)

npm install astro-seo
---
import { SEO } from 'astro-seo';
---

<head>
  <SEO
    title="Page Title"
    description="Page description"
    openGraph={{
      basic: {
        title: "OG Title",
        type: "website",
        image: "/og-image.png",
      }
    }}
    twitter={{ creator: "@handle" }}
  />
</head>

Handles meta tags, Open Graph, Twitter Cards, and canonical URLs automatically.

Essential Integrations

# Add integrations
npx astro add react      # React components
npx astro add tailwind   # Tailwind CSS
npx astro add mdx        # MDX support
npx astro add sitemap    # Auto-generate sitemap

# RSS feed
npm install @astrojs/rss

Troubleshooting

"Works locally but breaks on deploy"

  • Check environment variables are set on host
  • Verify base path configuration
  • Ensure Node version matches (v18+ recommended)

Dynamic routes missing pages

  • Verify getStaticPaths() returns all needed paths
  • Check for typos in params

Content Collection schema errors

  • Run astro check for validation details
  • Ensure frontmatter matches Zod schema exactly

Assets not loading

  • Use import for processed assets
  • Use public/ for unprocessed static files

References

For detailed guides on specific topics, see:

  • references/markdown-deep-dive.md - Advanced Markdown/MDX patterns
  • references/deployment-platforms.md - Platform-specific deployment details

Key Resources

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

33.86%
按下载量换算112

Claude

30.16%
按下载量换算100

Cursor

21.05%
按下载量换算70

Gemini CLI

9.26%
按下载量换算31

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

可疑

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills