docs: blog layout update (#8472)
Some checks failed
Canary / Prepare Version (push) Has been cancelled
Unused Dependencies / machete (push) Has been cancelled
CI / changes (push) Has been cancelled
CI / Build Rust Project on Windows (push) Has been cancelled
Deploy Documentation / deploy (push) Has been cancelled
Live Provider Tests / check-fork (push) Has been cancelled
Publish Ask AI Bot Docker Image / docker (push) Has been cancelled
Publish Docker Image / docker (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Canary / build-cli (push) Has been cancelled
Canary / Upload Install Script (push) Has been cancelled
Canary / bundle-desktop (push) Has been cancelled
Canary / bundle-desktop-intel (push) Has been cancelled
Canary / bundle-desktop-linux (push) Has been cancelled
Canary / bundle-desktop-windows (push) Has been cancelled
Live Provider Tests / changes (push) Has been cancelled
Canary / Release (push) Has been cancelled
CI / Check Rust Code Format (push) Has been cancelled
CI / Build and Test Rust Project (push) Has been cancelled
CI / Lint Rust Code (push) Has been cancelled
CI / Check Generated Schemas are Up-to-Date (push) Has been cancelled
CI / Test and Lint Electron Desktop App (push) Has been cancelled
Live Provider Tests / Build Binary (push) Has been cancelled
Live Provider Tests / Smoke Tests (push) Has been cancelled
Live Provider Tests / Smoke Tests (Code Execution) (push) Has been cancelled
Live Provider Tests / Compaction Tests (push) Has been cancelled
Live Provider Tests / goose server HTTP integration tests (push) Has been cancelled
Signed-off-by: Angie Jones <jones.angie@gmail.com> Co-authored-by: Angie Jones <jones.angie@gmail.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
|
|
@ -4,6 +4,7 @@ description: Learn how an AI agent embedded in GitHub Actions helps maintainers
|
|||
authors:
|
||||
- rizel
|
||||
- tyler
|
||||
image: /img/blog/goose-maintains-goose.png
|
||||
---
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ title: "How I Taught My Agent My Design Taste"
|
|||
description: "I used Agent Skills and recipes to automate execution so I could study taste, constraint design, feedback loops, and avoid AI smells."
|
||||
authors:
|
||||
- rizel
|
||||
image: /img/blog/automate-taste.png
|
||||
---
|
||||
|
||||

|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
---
|
||||
title: "Agent Guardrails and Controls: Applying the CORS Model to Agents"
|
||||
description: Applying the security model of CORS to Agentic technologies to address common attacks against tool calling.
|
||||
image: /img/blog/agentic_guardrails_header.jpg
|
||||
authors:
|
||||
- clinton
|
||||
- alex
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
In [our previous blog post](https://goose-docs.ai/blog/2025/03/31/securing-mcp/) we detailed the Model Context Protocol (MCP) system and discussed some security concerns and mitigations. As a brief recap, MCP provides agents with a means to accomplish tasks using defined tools; reducing the burden of using complex and varied APIs and integrations on the agent.
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ title: "goose Lands MCP Apps"
|
|||
description: "goose ships early support for the draft MCP Apps specification, aligning with the emerging standard for interactive UIs in MCP."
|
||||
authors:
|
||||
- aharvard
|
||||
image: /img/blog/goose-lands-mcp-apps-header-image.png
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
The MCP ecosystem is standardizing how servers deliver interactive UIs to hosts, and goose is an early adopter. Today we're shipping support for the draft MCP Apps specification ([SEP-1865](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx)), bringing goose in line with the emerging standard, as other hosts like Claude and ChatGPT move toward adoption.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
title: "Why Tool Descriptions Aren’t Enough"
|
||||
description: "I thought better tool descriptions would solve everything. They didn’t. Here’s what finally made MCP sampling click for me."
|
||||
image: /img/blog/tool-descriptions-banner.png
|
||||
authors:
|
||||
- ebony
|
||||
---
|
||||

|
||||

|
||||
|
||||
|
||||
The first question I had when I heard about MCP sampling was:
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: "goose mobile apps and agent clients"
|
||||
description: Consolidating agent apps for iOS and Android and ACP
|
||||
image: /img/blog/goose-mobile-apps-banner.png
|
||||
authors:
|
||||
- mic
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
In 2025 we did a fairly cutting edge take on whole device automation using Android (code name was gosling) which was an on-device agent that would take over your device (mic even used it to do some shopping - which he realized after some things arrived at his door that it had automatically purchased as the result of an email - hence the PoC/experimental label!)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: "From MCP-UI to MCP Apps: Evolving Interactive Agent UIs"
|
||||
description: "I migrated a real MCP-UI server to MCP Apps. Here’s what actually changed, what broke, and why this shift matters."
|
||||
image: /img/blog/mcp-ui-to-apps-blogbanner.png
|
||||
authors:
|
||||
- ebony
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
MCP-UI is fun. It’s scrappy. It’s early. And like I said in my last post, there’s something genuinely addictive about building this close to the edges of an ecosystem while everything is still taking shape.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
---
|
||||
title: "5 Tips for Building MCP Apps That Work"
|
||||
description: "5 expert tips on building better MCP Apps for your AI agents"
|
||||
image: /img/blog/mcp-apps-tips-blogbanner.png
|
||||
authors:
|
||||
- rizel
|
||||
- matt
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
[MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps) allow you to render interactive UI directly inside any agent supporting the Model Context Protocol. Instead of a wall of text, your agent can now provide a functional chart, a checkout form, or a video player. This bridges the gap in agentic workflows: clicking a button is often clearer than describing the action you hope an agent executes.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: "8 Things You Didn't Know About Code Mode"
|
||||
description: Discover how Code mode reduces context rot and token usage in AI agents making them more efficient for long running sessions.
|
||||
image: /img/blog/code-mode-header-image.png
|
||||
authors:
|
||||
- rizel
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
Agents fundamentally changed how we program. They enable developers to move faster by disintermediating the traditional development workflow. This means less time switching between specialized tools and fewer dependencies on other teams. Now that agents can execute complicated tasks, developers face a new challenge: using them effectively over long sessions.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@
|
|||
title: "Level Up Your AI Game with rp-why"
|
||||
description: "A goose skill that measures the cognitive complexity of your AI collaboration using the Gas Town × DOK framework."
|
||||
date: 2026-02-06
|
||||
image: /img/blog/rp-why-banner.png
|
||||
authors:
|
||||
- dakota
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
## What is rp-why?
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: "How I Used RPI to Build an OpenClaw Alternative"
|
||||
description: "Learn how I built a minimal, personal AI agent using goose and the RPI method."
|
||||
image: /img/blog/rpi-openclaw-blogbanner.png
|
||||
authors:
|
||||
- rizel
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
Everyone on Tech Twitter has been buying Mac Minis, so they could run a local agentic tool called [OpenClaw](https://openclaw.ai/). OpenClaw is a messaging-based AI assistant that connects to platforms such as Discord and Telegram allowing you to interact with an AI agent through DMs or @mentions. Under the hood, it uses an agent called Pi to execute tasks, browse the web, write code, and more.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: "One Shot Prompting is Dead"
|
||||
description: "Practical steps and mental models for building context engineered workflows instead of clever prompts."
|
||||
image: /img/blog/context-engineering-blogbanner.png
|
||||
authors:
|
||||
- ebony
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
I attended one shot prompting’s funeral.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
---
|
||||
title: "Gas Town Explained: How to Use Goosetown for Parallel Agentic Engineering"
|
||||
description: "Learn how Gas Town and Goosetown lead the industrial coding revolution by teaching AI agents to work together in a team. This beginner guide explains the infrastructure we're using to move from talking to one AI to coordinating many agents at once."
|
||||
image: /img/blog/goosetown.png
|
||||
authors:
|
||||
- rizel
|
||||
- tyler
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
On New Year's Day 2026, while many were recovering from the night before, a different kind of hangover took hold of every AI-pilled, chronically online software engineer. Steve Yegge published a new blog post: "[Welcome to Gas Town](https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04)." Some walked away inspired to finally use their agents optimally; others were just plain confused. If you're like me, you felt a bit of both.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ title: "goose v1.25.0: Sandboxed, Streamlined, and More Secure"
|
|||
description: "goose v1.25.0 brings macOS sandboxing, a unified summon extension, rich MCP app UIs, agentic CLI upgrades, and SLSA build provenance."
|
||||
authors:
|
||||
- debbie
|
||||
image: /img/blog/goose-v1-25-0.png
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
goose v1.25.0 is here, and it's one of our most significant releases yet. This version brings macOS sandboxing for enhanced security, a major architectural simplification with the unified summon extension, rich UI rendering for MCP apps, and a wave of improvements to agentic CLI providers. Whether you're running goose Desktop or the CLI, there's something in this release for you.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ title: "Grant Winner: Goose In A Pond"
|
|||
description: "Introducing a privacy-first, local AI home assistant powered by Goose on edge hardware."
|
||||
authors:
|
||||
- angie
|
||||
image: /img/blog/goose-grant-goose-in-a-pond.png
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
We launched the [goose grant program](/grants/) awarding $100K grants for developers building the future of agentic AI. We're looking for ambitious, open source projects that push goose into new territory, and today, We're thrilled to introduce one of our grant recipients: **Goose In A Pond**, a project that's taking goose off the desktop and into your home.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ title: "Order Lunch Without Leaving Your AI Agent"
|
|||
description: "Use the Neighborhood extension in goose to discover nearby restaurants, browse interactive menus, and place a takeout order, all from a simple chat prompt."
|
||||
authors:
|
||||
- debbie
|
||||
image: /img/blog/lunch-with-goose.png
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
If you're anything like me, deciding what to eat for lunch is harder than it should be. Now add dietary restrictions on top of that (I'm coeliac so have to eat gluten-free) and suddenly finding a restaurant becomes a whole research project. Searching menus, cross-referencing reviews, checking if that one sandwich actually has gluten in it... it's exhausting.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ title: "WebMCP for Beginners"
|
|||
description: "WebMCP lets websites expose structured actions that AI agents can call directly. This guide explains how it works, how it differs from MCP and browser automation, and how to build your own WebMCP-enabled site."
|
||||
authors:
|
||||
- rizel
|
||||
image: /img/blog/webmcp-for-beginners.png
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
Raise your hand if you thought WebMCP was just an MCP server. Guilty as charged. I did too. It turns out it's a W3C standard that uses similar concepts to MCP. Here's what it actually is.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
---
|
||||
title: "Adversary Agent: using a hidden agent to keep the main agent safe"
|
||||
description: "Introducing adversary mode — an independent agent reviewer that silently watches the main agent to keep it away from danger."
|
||||
image: /img/blog/adversary-mode.png
|
||||
authors:
|
||||
- mic
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
One of the desires of goose (well for some of us) was to avoid the constant asking for permissions, delegating all the decisions to end users in an attempt to keep agent execution of tools safe. Sometimes that gets pretty noisy and annoying and ends up being less secure when you get tired of reading and approving.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ title: "goose 2.0 beta - new architecture and clients"
|
|||
description: "We're shipping a new TUI, rewriting the desktop app in Tauri, and unifying everything under ACP."
|
||||
authors:
|
||||
- alexhancock
|
||||
featured: true
|
||||
image: /img/blog/goose-2-blog-cover.jpg
|
||||
---
|
||||
|
||||
# goose 2.0 beta - new architecture and clients
|
||||
|
||||

|
||||

|
||||
|
||||
goose started life in the terminal. The earliest versions were a Python CLI that ran the agent in-process — you typed a message, the model responded, tools executed, and everything happened in a single loop. That simplicity was a strength: it meant anyone with a terminal could start using goose immediately, no app to install, no server to run.
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ title: "How to Break Up with Your Agent"
|
|||
description: "ACP lets you keep your favorite editor but swap the AI agent, or keep your agent but use it from any editor. Here's what actually works today in Goose."
|
||||
authors:
|
||||
- codefromthecrypt
|
||||
image: /img/blog/how-to-break-up-with-your-agent.png
|
||||
---
|
||||
|
||||

|
||||

|
||||
|
||||
The biggest shift in developer tooling over the last year wasn't the rise of agents. It was the rise of agent subscriptions. We stopped choosing LLM platforms and counting tokens. We started choosing an agent CLI and paying a flat monthly fee.
|
||||
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ const config: Config = {
|
|||
onInlineAuthors: "warn",
|
||||
onUntruncatedBlogPosts: "warn",
|
||||
blogSidebarCount: "ALL",
|
||||
postsPerPage: 22,
|
||||
},
|
||||
theme: {
|
||||
customCss: [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
import React from 'react';
|
||||
import Head from '@docusaurus/Head';
|
||||
import {useBlogListPageStructuredData} from '@docusaurus/plugin-content-blog/client';
|
||||
export default function BlogListPageStructuredData(props) {
|
||||
const structuredData = useBlogListPageStructuredData(props);
|
||||
return (
|
||||
<Head>
|
||||
<script type="application/ld+json">
|
||||
{JSON.stringify(structuredData)}
|
||||
</script>
|
||||
</Head>
|
||||
);
|
||||
}
|
||||
185
documentation/src/theme/BlogListPage/index.tsx
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
import React, {type ReactNode} from 'react';
|
||||
import clsx from 'clsx';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||
import {
|
||||
PageMetadata,
|
||||
HtmlClassNameProvider,
|
||||
ThemeClassNames,
|
||||
} from '@docusaurus/theme-common';
|
||||
import BlogLayout from '@theme/BlogLayout';
|
||||
import BlogListPaginator from '@theme/BlogListPaginator';
|
||||
import SearchMetadata from '@theme/SearchMetadata';
|
||||
import type {Props} from '@theme/BlogListPage';
|
||||
import BlogListPageStructuredData from '@theme/BlogListPage/StructuredData';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
function BlogListPageMetadata(props: Props): ReactNode {
|
||||
const {metadata} = props;
|
||||
const {
|
||||
siteConfig: {title: siteTitle},
|
||||
} = useDocusaurusContext();
|
||||
const {blogDescription, blogTitle, permalink} = metadata;
|
||||
const isBlogOnlyMode = permalink === '/';
|
||||
const title = isBlogOnlyMode ? siteTitle : blogTitle;
|
||||
return (
|
||||
<>
|
||||
<PageMetadata title={title} description={blogDescription} />
|
||||
<SearchMetadata tag="blog_posts_list" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const getAuthorName = (author: any): string =>
|
||||
typeof author === 'string' ? author : (author.name || author.key || author);
|
||||
|
||||
function AuthorDisplay({ authors }: { authors: any[] }) {
|
||||
if (!authors?.length) return null;
|
||||
|
||||
const authorsToDisplay = authors.slice(0, 3);
|
||||
const hasMore = authors.length > 3;
|
||||
const hasResolvedAuthors = authorsToDisplay.some(author =>
|
||||
typeof author === 'object' && (author.imageURL || author.image_url)
|
||||
);
|
||||
|
||||
if (hasResolvedAuthors) {
|
||||
return (
|
||||
<div className={styles.postAuthors}>
|
||||
{authorsToDisplay.map((author, index) => (
|
||||
<div key={index} className={styles.authorInfo}>
|
||||
{(author.imageURL || author.image_url) && (
|
||||
<img
|
||||
src={author.imageURL || author.image_url}
|
||||
alt={getAuthorName(author)}
|
||||
className={styles.authorAvatar}
|
||||
/>
|
||||
)}
|
||||
<span className={styles.authorName}>{getAuthorName(author)}</span>
|
||||
</div>
|
||||
))}
|
||||
{hasMore && <span className={styles.authorName}>+{authors.length - 3} more</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const authorNames = authorsToDisplay.map(getAuthorName);
|
||||
const displayText = authorNames.join(', ') + (hasMore ? `, +${authors.length - 3} more` : '');
|
||||
|
||||
return (
|
||||
<div className={styles.postAuthors}>
|
||||
<span className={styles.authorName}>{displayText}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FeaturedPost({ post }: { post: any }) {
|
||||
const url = useBaseUrl(post.content.metadata.permalink);
|
||||
const imageUrl = post.content.frontMatter.image ? useBaseUrl(post.content.frontMatter.image) : null;
|
||||
const title = post.content.metadata.title;
|
||||
const formattedDate = post.content.metadata.formattedDate;
|
||||
const description = post.content.metadata.description || post.content.frontMatter.description;
|
||||
const authors = post.content?.metadata?.authors || post.content?.frontMatter?.authors || [];
|
||||
|
||||
return (
|
||||
<article className={styles.featuredPost}>
|
||||
<div className={styles.featuredContent}>
|
||||
<div className={styles.featuredDate}>{formattedDate}</div>
|
||||
<h2 className={styles.featuredTitle}>
|
||||
<a href={url}>{title}</a>
|
||||
</h2>
|
||||
<AuthorDisplay authors={authors} />
|
||||
<div className={styles.featuredDescription}>{description}</div>
|
||||
<a href={url} className={styles.featuredButton}>Read full article</a>
|
||||
</div>
|
||||
{imageUrl && (
|
||||
<div className={styles.featuredImage}>
|
||||
<img src={imageUrl} alt={title} />
|
||||
</div>
|
||||
)}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
function BlogPostCard({ post }: { post: any }) {
|
||||
const url = useBaseUrl(post.content.metadata.permalink);
|
||||
const imageUrl = post.content.frontMatter.image ? useBaseUrl(post.content.frontMatter.image) : null;
|
||||
const title = post.content.metadata.title;
|
||||
const formattedDate = post.content.metadata.formattedDate;
|
||||
const description = post.content.metadata.description || post.content.frontMatter.description;
|
||||
const authors = post.content?.metadata?.authors || post.content?.frontMatter?.authors || [];
|
||||
|
||||
return (
|
||||
<article className={styles.postCard}>
|
||||
{imageUrl && (
|
||||
<div className={styles.postImage}>
|
||||
<img src={imageUrl} alt={title} />
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.postContent}>
|
||||
<div className={styles.postDate}>{formattedDate}</div>
|
||||
<h3 className={styles.postTitle}>
|
||||
<a href={url}>{title}</a>
|
||||
</h3>
|
||||
<AuthorDisplay authors={authors} />
|
||||
<div className={styles.postDescription}>{description}</div>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
function BlogPostGrid({ posts }: { posts: any[] }) {
|
||||
return (
|
||||
<div className={styles.postsGrid}>
|
||||
{posts.map((post, index) => (
|
||||
<BlogPostCard key={index} post={post} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlogListPageContent(props: Props): ReactNode {
|
||||
const { metadata, items } = props;
|
||||
const isFirstPage = !metadata.permalink.includes('/page/');
|
||||
|
||||
const validItems = items.filter(item =>
|
||||
item.content?.metadata?.title && item.content?.frontMatter
|
||||
);
|
||||
|
||||
const featuredPosts = isFirstPage
|
||||
? validItems.filter(item => item.content.frontMatter.featured === true)
|
||||
: [];
|
||||
|
||||
const regularPosts = isFirstPage
|
||||
? validItems.filter(item => item !== featuredPosts[0])
|
||||
: validItems;
|
||||
|
||||
return (
|
||||
<BlogLayout sidebar={undefined}>
|
||||
<div className={styles.blogContainer}>
|
||||
{featuredPosts.length > 0 && (
|
||||
<div className={styles.featuredSection}>
|
||||
<FeaturedPost post={featuredPosts[0]} />
|
||||
</div>
|
||||
)}
|
||||
{regularPosts.length > 0 && <BlogPostGrid posts={regularPosts} />}
|
||||
<div className={styles.paginationWrapper}>
|
||||
<BlogListPaginator metadata={metadata} />
|
||||
</div>
|
||||
</div>
|
||||
</BlogLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BlogListPage(props: Props): ReactNode {
|
||||
return (
|
||||
<HtmlClassNameProvider
|
||||
className={clsx(
|
||||
ThemeClassNames.wrapper.blogPages,
|
||||
ThemeClassNames.page.blogListPage,
|
||||
)}>
|
||||
<BlogListPageMetadata {...props} />
|
||||
<BlogListPageStructuredData {...props} />
|
||||
<BlogListPageContent {...props} />
|
||||
</HtmlClassNameProvider>
|
||||
);
|
||||
}
|
||||
258
documentation/src/theme/BlogListPage/styles.module.css
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
/* Magazine-style Blog Layout */
|
||||
|
||||
.blogContainer {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 1rem;
|
||||
}
|
||||
|
||||
.featuredSection {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.featuredPost {
|
||||
background: var(--ifm-card-background-color);
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 2rem;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: box-shadow 0.3s ease;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.featuredPost:hover {
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.featuredContent {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.featuredImage {
|
||||
flex: 0 0 350px;
|
||||
order: 2;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.featuredImage img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.featuredDate {
|
||||
font-size: 0.9rem;
|
||||
color: var(--ifm-color-content-secondary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.featuredTitle {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--ifm-color-content);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.featuredTitle a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.featuredTitle a:hover {
|
||||
color: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
|
||||
.postAuthors {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.authorInfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.authorAvatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
}
|
||||
|
||||
.authorName {
|
||||
font-size: 0.85rem;
|
||||
color: var(--ifm-color-content-secondary);
|
||||
}
|
||||
|
||||
.featuredDescription {
|
||||
color: var(--ifm-color-content-secondary);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1.5rem;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.featuredButton {
|
||||
align-self: flex-start;
|
||||
background: var(--ifm-color-primary);
|
||||
color: white;
|
||||
padding: 0.5rem 1rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
font-size: 0.9rem;
|
||||
transition: background-color 0.3s ease;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.featuredButton:hover {
|
||||
background: var(--ifm-color-primary-dark);
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.postsGrid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.postCard {
|
||||
background: var(--ifm-card-background-color);
|
||||
border: 1px solid var(--ifm-color-emphasis-200);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: box-shadow 0.3s ease;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.postCard:hover {
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.postImage {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.postImage img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.postContent {
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.postDate {
|
||||
font-size: 0.85rem;
|
||||
color: var(--ifm-color-content-secondary);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.postTitle {
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.75rem;
|
||||
color: var(--ifm-color-content);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.postTitle a {
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.postTitle a:hover {
|
||||
color: var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
|
||||
.postDescription {
|
||||
color: var(--ifm-color-content-secondary);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1rem;
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
.paginationWrapper {
|
||||
margin-top: 2rem;
|
||||
padding-top: 2rem;
|
||||
border-top: 1px solid var(--ifm-color-emphasis-200);
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 996px) {
|
||||
.postsGrid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.featuredPost {
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.featuredImage {
|
||||
order: 0;
|
||||
flex: none;
|
||||
}
|
||||
|
||||
.featuredImage img {
|
||||
height: 250px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.postsGrid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.blogContainer {
|
||||
padding: 0 0.5rem;
|
||||
}
|
||||
|
||||
.featuredPost {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.featuredTitle {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.postContent {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.featuredPost {
|
||||
padding: 1rem;
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 315 KiB |
|
Before Width: | Height: | Size: 378 KiB After Width: | Height: | Size: 378 KiB |
BIN
documentation/static/img/blog/automate-taste.png
Normal file
|
After Width: | Height: | Size: 237 KiB |
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
BIN
documentation/static/img/blog/context-engineering-blogbanner.png
Normal file
|
After Width: | Height: | Size: 773 KiB |
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 679 KiB After Width: | Height: | Size: 679 KiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
BIN
documentation/static/img/blog/goose-maintains-goose.png
Normal file
|
After Width: | Height: | Size: 377 KiB |
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
BIN
documentation/static/img/blog/goosetown.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 3.1 MiB After Width: | Height: | Size: 3.1 MiB |
|
Before Width: | Height: | Size: 258 KiB After Width: | Height: | Size: 258 KiB |
|
Before Width: | Height: | Size: 239 KiB After Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 572 KiB After Width: | Height: | Size: 572 KiB |
BIN
documentation/static/img/blog/rpi-openclaw-blogbanner.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 766 KiB After Width: | Height: | Size: 766 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |