Skip to content
Back to skills

Ogee

ASecurity

Fix and design link previews (Open Graph tags, preview cards, sitemap, llms.txt) for websites, especially single-page apps (React, Vite, Vue, Lovable, Bolt, v0) where every link shows the same preview. Use when someone wants their links to look good on WhatsApp, X, LinkedIn, Slack or iMessage, wants OG images, or asks why their shared links all look the same.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 14, 2026
developmentjavascriptgojavareactvueapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 14, 2026

npx -y skills add virus-dev-official/ogee --skill ogee --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ogee?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ogee
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/virus-dev-official-ogee/badge)](https://www.skillsdirectory.com/skills/virus-dev-official-ogee)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ogee
description: Fix and design link previews (Open Graph tags, preview cards, sitemap, llms.txt) for websites, especially single-page apps (React, Vite, Vue, Lovable, Bolt, v0) where every link shows the same preview. Use when someone wants their links to look good on WhatsApp, X, LinkedIn, Slack or iMessage, wants OG images, or asks why their shared links all look the same.
---

# ogee

ogee runs a Cloudflare Worker in front of a site and gives every page its own <head> and a preview card
drawn in the site's brand. You design the cards; ogee's CLI collects evidence and renders what you write.

## Getting started

1. `npx @virusdev/ogee check <url>`: see what's wrong with a link's preview today (`--json` for details).
2. `npx @virusdev/ogee init <url>`: makes an `ogee/` folder with the config, Worker, logo and fonts.
   Use `--origin <url>` when the site is hosted somewhere the Worker can't sit in front of (Lovable, Vercel…).
3. `cd ogee && npx @virusdev/ogee inspect --for-agent --json`: screenshots and brand data. Read the AGENT.md it writes.
4. Design, `npx @virusdev/ogee render --json`, look at every PNG, repeat.
5. `npx @virusdev/ogee dev` to try the Worker locally; `npx @virusdev/ogee deploy` only when the person says so.

## How to make great cards

1. **Look at the site.** Open the screenshots (home.png, home-mobile.png and the other pages). Note the
   background, the accent colors, the type, any signature shapes (blobs, grids, gradients, noise, outlines).
2. **Check the brand** in `ogee.config.ts` against what you saw: colors, fonts, logo. Fix what's off.
   `brand.json` has the raw values the scanner found.
3. **Pick the approach:**
   - A built-in template (`minimal`, `glow`, `split`, `bold`) with the right colors, if one already fits.
   - A custom template that echoes the site's signature look. This is usually what makes cards feel like
     the site and not like a generic tool.
   - A plate, when the look can't be drawn by the card renderer.
4. **Give pages their pictures.** Product, post and portfolio pages should show their own image: set
   `image` on the route (or return it from a route function, from your API). The built-in templates frame
   it beside the text. Pages with `ogImage` already have a designed card and are used as they are.
5. **Write real page text.** Every route in `routes` needs a specific title and a description that would make
   someone click. Not the site tagline repeated, not "Home".
6. **Render and look.** Run `npx @virusdev/ogee render --json`, then open every PNG it lists. Check: nothing cut off,
   text readable at small sizes (cards show at ~500px wide in chats), the logo clear, the brand obvious.
   Long titles must still fit. Fix and render again until every card is good.
7. **Show the person** the gallery (`.ogee/cards/index.html`). Don't deploy without asking them.

## Writing a card template

A template is a function that takes `card` and returns an HTML string. Put it in
`templates/<name>.ts`, register it in `ogee.config.ts`, and select it:

```ts
// templates/brand.ts
import { esc, fitTitle, alpha, type Template } from "@virusdev/ogee";

export const brand: Template = (card) => `
<div style="position:relative;display:flex;width:${card.width}px;height:${card.height}px;background:${card.colors.background};overflow:hidden">
  <!-- background layers first, all position:absolute -->
  <div style="position:absolute;left:-120px;top:-160px;width:620px;height:620px;border-radius:9999px;background:${card.colors.accent};filter:blur(120px);opacity:.8"></div>
  <!-- content last, position:relative, so it paints on top -->
  <div style="position:relative;display:flex;flex-direction:column;justify-content:flex-end;width:100%;height:100%;padding:72px 80px">
    <div style="font-family:'${card.headingFont}';font-size:${fitTitle(card.title)}px;font-weight:700;line-height:1.06;color:${card.colors.foreground};text-wrap:balance">${esc(card.title)}</div>
  </div>
</div>`;
```

```ts
// ogee.config.ts
import { brand } from "./templates/brand";
export default defineConfig({ /* … */ templates: { brand }, card: { template: "brand" } });
```

`card` has: `title`, `description`, `eyebrow`, `siteName`, `host`, `path`, `url`,
`image` and `imageAspect` (the page's own picture: product photo, cover, screenshot, flyer; below 1 it's
portrait), `logo` and `logoAspect` (width ÷ height; above ~2.2 it's a wordmark),
`plate` (background image, if any), `colors` (`background`, `foreground`, `accent`, `accent2`, `muted`),
`headingFont`, `bodyFont`, `width`, `height` (1200×630), `seed` and `rand(i)` (stable per page, for
variety), and `data` (whatever the route put in `data`).
Use `<img src="${card.logo}">`, `<img src="${card.plate}">` and `<img src="${card.image}">` as they are.

Helpers from "@virusdev/ogee": `esc` (always escape text), `fitTitle(title, maxPx)` (size that fits),
`clip(text, chars)` (shorten at a word), `alpha(color, 0.5)`, `readableOn(bg)` (black or white), `isDark(color)`.

### What the renderer supports (takumi, not a browser)
- Layout: flexbox and block. Give every element with more than one child `display:flex` to be safe.
- Works: linear/radial/conic gradients, `filter: blur()`, `opacity`, `box-shadow`, `text-shadow`, `border-radius`,
  `mix-blend-mode`, `mask-image`, `clip-path`, `background-clip:text`, `text-wrap: balance`, inline SVG
  and SVG filters (feTurbulence for grain), `transform`.
- Stacking: CSS paint order. Absolutely positioned elements paint over normal ones, so put content in a
  `position:relative` box *after* the background layers.
- Don't use `line-clamp` on text that might fit exactly (it drops the last line). Use `clip()` instead.
- No external CSS, no web fonts by URL, no JavaScript. Fonts must be listed in `brand.fonts` (files in `assets/`).
- If the look needs something the renderer can't draw (WebGL, complex CSS, photos composited with text),
  make a **plate**: write `plate.html` with the background only (no text), run `npx @virusdev/ogee plate plate.html`,
  set `brand.plate: "plate.png"`, and draw the text on top in the template.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.