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>
This commit is contained in:
Adewale Abati 2026-04-11 01:26:49 +02:00 committed by GitHub
parent 0e743112ea
commit 3f5277538d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 502 additions and 18 deletions

View file

@ -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
---
![blog cover](goose-maintains-goose.png)

View file

@ -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
---
![blog cover](automate-taste.png)

View file

@ -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
---
![blog cover](agentic_guardrails_header.jpg)
![blog cover](/img/blog/agentic_guardrails_header.jpg)
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.

View file

@ -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
---
![Retro 1980s hardware lab with three CRT monitors displaying "goose Lands MCP Apps" in glowing green text, with a small goose figurine on the desk](goose-lands-mcp-apps-header-image.png)
![Retro 1980s hardware lab with three CRT monitors displaying "goose Lands MCP Apps" in glowing green text, with a small goose figurine on the desk](/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.

View file

@ -1,10 +1,11 @@
---
title: "Why Tool Descriptions Arent Enough"
description: "I thought better tool descriptions would solve everything. They didnt. Heres what finally made MCP sampling click for me."
image: /img/blog/tool-descriptions-banner.png
authors:
- ebony
---
![blog banner](blogbanner.png)
![blog banner](/img/blog/tool-descriptions-banner.png)
The first question I had when I heard about MCP sampling was:

View file

@ -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
---
![goose mobile apps](goose-mobile-apps-banner.png)
![goose mobile apps](/img/blog/goose-mobile-apps-banner.png)
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!)

View file

@ -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. Heres what actually changed, what broke, and why this shift matters."
image: /img/blog/mcp-ui-to-apps-blogbanner.png
authors:
- ebony
---
![blog banner](blogbanner.png)
![blog banner](/img/blog/mcp-ui-to-apps-blogbanner.png)
MCP-UI is fun. Its scrappy. Its early. And like I said in my last post, theres something genuinely addictive about building this close to the edges of an ecosystem while everything is still taking shape.

View file

@ -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
---
![Level Up Your MCP Apps - goose and MCP Jam](blogbanner.png)
![Level Up Your MCP Apps - goose and MCP Jam](/img/blog/mcp-apps-tips-blogbanner.png)
[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.

View file

@ -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
---
![blog cover](header-image.png)
![blog cover](/img/blog/code-mode-header-image.png)
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.

View file

@ -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
---
![rp-why skill banner](rp-why-banner.png)
![rp-why skill banner](/img/blog/rp-why-banner.png)
## What is rp-why?

View file

@ -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
---
![How I Used RPI to Build an OpenClaw Alternative](blogbanner.png)
![How I Used RPI to Build an OpenClaw Alternative](/img/blog/rpi-openclaw-blogbanner.png)
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.

View file

@ -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
---
![One shot prompting is dead](blogbanner.png)
![One shot prompting is dead](/img/blog/context-engineering-blogbanner.png)
I attended one shot promptings funeral.

View file

@ -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
---
![Goosetown](goosetown.png)
![Goosetown](/img/blog/goosetown.png)
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.

View file

@ -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
---
![Banner image for the goose v1.25.0 release](banner.png)
![Banner image for the goose v1.25.0 release](/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.

View file

@ -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
---
![blog banner](banner.png)
![blog banner](/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.

View file

@ -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
---
![Ba'al Falafel salads menu in goose showing Couscous Salad, Red Cabbage Salad, and Beets Apple Salad with photos and prices](banner.png)
![Ba'al Falafel salads menu in goose showing Couscous Salad, Red Cabbage Salad, and Beets Apple Salad with photos and prices](/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.

View file

@ -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
---
![blog cover](webmcp-for-beginners.png)
![blog cover](/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.

View file

@ -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
---
![blog cover](adversary-mode.png)
![blog cover](/img/blog/adversary-mode.png)
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.

View file

@ -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
![blog cover](goose-2-blog-cover.jpg)
![blog cover](/img/blog/goose-2-blog-cover.jpg)
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.

View file

@ -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
---
![Editors connect to Goose via ACP, and Goose connects to multiple agents](header.png)
![Editors connect to Goose via ACP, and Goose connects to multiple agents](/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.

View file

@ -75,6 +75,7 @@ const config: Config = {
onInlineAuthors: "warn",
onUntruncatedBlogPosts: "warn",
blogSidebarCount: "ALL",
postsPerPage: 22,
},
theme: {
customCss: [

View file

@ -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>
);
}

View 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>
);
}

View 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;
}
}

View file

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 315 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 773 KiB

View file

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 679 KiB

After

Width:  |  Height:  |  Size: 679 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 377 KiB

View file

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

View file

Before

Width:  |  Height:  |  Size: 3.1 MiB

After

Width:  |  Height:  |  Size: 3.1 MiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 258 KiB

After

Width:  |  Height:  |  Size: 258 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 572 KiB

After

Width:  |  Height:  |  Size: 572 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

View file

Before

Width:  |  Height:  |  Size: 766 KiB

After

Width:  |  Height:  |  Size: 766 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After