Skip to content
Back to skills

Kortix Presentation

ASecurity

Recipe: build, edit, present or record a Kortix deck or film (a Next.js route under /presentations in apps/web, never a file). Load kortix-brand first. This skill covers the slide engine, builds, presenter notes, films and QA. Load WHENEVER the user asks for a deck, slides, a presentation, a walkthrough, a talk track, a guided demo, presenter notes, or a product video; and whenever editing anything under apps/web/src/app/[locale]/presentations.

  • 20,239 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 29, 2026
ai-agentsgoshellbashreactnextjsnoderailsgitapisecurity

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned October 7, 2026

npx -y skills add kortix-ai/suna --skill kortix-presentation --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Kortix Presentation?

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

Security grade badge for Kortix Presentation
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kortix-ai-kortix-presentation/badge)](https://www.skillsdirectory.com/skills/kortix-ai-kortix-presentation)

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: kortix-presentation
description: "Recipe: build, edit, present or record a Kortix deck or film (a Next.js route under /presentations in apps/web, never a file). Load kortix-brand first. This skill covers the slide engine, builds, presenter notes, films and QA. Load WHENEVER the user asks for a deck, slides, a presentation, a walkthrough, a talk track, a guided demo, presenter notes, or a product video; and whenever editing anything under apps/web/src/app/[locale]/presentations."
---

# Kortix Presentations

**Load [`kortix-brand`](../kortix-brand/SKILL.md) first.** It owns color, type, layout (deck column), motion, voice and claims. This file owns the slide engine and the procedure.

**A Kortix deck is a Next.js route.** Every presentation lives at
`/presentations/<slug>` in `apps/web`, styled like the marketing site, driven by
the keyboard, and built to be screen recorded.

There is no file format. Do not produce `.pptx`, Keynote, Google Slides, or a
Markdown "slides" file — that output gets rejected on sight, and nothing in this
skill will help you make one. A deck is a React component: it themes correctly,
stays sharp at any projection size, is reviewed in a PR, and cannot drift from
the product copy because it imports the same content modules the marketing pages
do.

## Where everything is

```
apps/web/src/app/[locale]/presentations/
  layout.tsx        full-bleed shell, noindex
  page.tsx          the index — every deck, one card each
  index-client.tsx  the index UI (client: reads each deck's slides for counts)
  registry.ts       the ONE file you edit to add a deck
  [deck]/
    page.tsx        server route: generateStaticParams + generateMetadata
    deck-client.tsx resolves slug → deck, mounts the engine
  decks/<slug>.tsx  a deck: `useSlides(): SlideDef[]`
  engine/
    deck.tsx        keyboard engine, build steps, presenter notes, overview
    parts.tsx       Slide, SectionHead, Spine, Panel, RowList, Shot, Rise…
    diagram.tsx     build-aware mechanism diagrams
  README.md         the authoring guide this skill summarises
```

Shipped decks: `security` (diagram-led — **the reference implementation, read it
first**), `platform`, `sales`. Legacy `/presentation` paths 307 to their new
homes via `next.config.ts`. The tree is allowlisted public in `src/middleware.ts`
— decks are link-shared and `noindex`, never behind a login.

## Using a deck

Open `/presentations`, pick one, press `F`.

| Key | Does |
| --- | --- |
| `→` `Space` `J` | next build step, or next slide |
| `←` `K` | back one step, or previous slide fully built |
| `Home` `End` | first / last slide |
| `1`–`9` | jump to slide |
| `G` `Esc` | overview grid (every slide, fully built) |
| `F` | fullscreen |
| `N` | presenter notes — the spoken script |

**Recording:** open the deck, press `F`, record the tab. Read the script first
with `N` → **Full script**, then close the drawer — notes never render on the
stage, so the recording stays clean. Prefer light theme on decks that embed
product screenshots, since the screenshots are light. Reload before you record:
after a hot reload in dev, accumulated keydown listeners make one press jump
several steps.

## Films — launch and product videos

A video is not a screen recording of a deck. It is a **film**: a route at
`/presentations/film/<slug>` where every frame is a pure function of the frame
number. The same React tree plays live (it is the hero of `/launch`) and renders
to MP4 through headless Chrome and ffmpeg, with a synthesized score and sound
effects locked to the bar grid (60 fps, 120 BPM, one bar = 120 frames).

```bash
open http://localhost:<web>/presentations/film/launch           # live, scrubbable
open "http://localhost:<web>/presentations/film/launch?frame=1800"  # one frame
bun apps/web/scripts/film/render.ts launch --url http://localhost:<web>        # 1080p60 MP4
bun apps/web/scripts/film/render.ts launch --url http://localhost:<web> --audio-only
```

The MP4 lands in the gitignored `output/film/<slug>/`. Never edit a film file
while a render runs: hot reload reaches the render pages. The engine, the
storyboard, the motion rules, the sound pipeline and the verification bar:
**`references/films.md`**. Read it before touching a film.

## Adding a deck — two edits

**1. `decks/<slug>.tsx`** — a client component exporting `useSlides()`:

```tsx
'use client';

import type { SlideDef } from '../engine/deck';
import { Slide, Rise, Spine, Panel, Dim } from '../engine/parts';

export function useSlides(): SlideDef[] {
  return [
    {
      id: 'title',
      label: 'Chapter name',
      notes: 'What you say while this is on screen.',
      node: (
        <Slide>
          <Rise i={0}>
            <h1 className="text-foreground text-5xl font-medium tracking-tight">
              The claim
            </h1>
          </Rise>
        </Slide>
      ),
    },
  ];
}
```

**2. A row in `DECKS` in `registry.ts`:**

```ts
{
  slug: 'connectors',
  title: 'Connectors walkthrough',
  description: 'One line for the index card and the route metadata.',
  kind: 'Product',
  tags: ['Diagram-led'],
  useSlides: useConnectorsSlides,
}
```

There is no step 3. The route, the index card, the slide and build counts, the
length estimate and the page metadata are all derived from those two edits.

## The slide API

```tsx
type SlideDef = {
  id: string;          // stable, unique within the deck
  label: string;       // shown bottom-left on the stage
  node: ReactNode | ((step: number) => ReactNode);
  steps?: number;      // extra build steps. Total → presses = steps + 1
  notes?: string | readonly string[];  // the spoken script
};
```

## Builds — the format Kortix decks use

`steps: 3` means `→` is pressed four times on that slide, and `node(step)` lights
up one more part of the picture each time. Advancing past the last step moves on;
reversing off step 0 lands on the previous slide at *its* last step, so `←`
always undoes exactly what `→` just did.

This is the point of the format: **a diagram assembles while it is explained,
instead of landing all at once.** Reach for it whenever you are explaining a
mechanism — a request crossing a boundary, a branch becoming a merge, a call
being held and released.

**The hard rule: never mount or unmount on a build step.** Every element is in
the DOM from the first frame, ghosted, and a step raises its opacity. If parts
appeared instead, every press would reflow the slide and the viewer would lose
the thread.

```tsx
// ✅ opacity — layout is stable, the eye tracks what changed
<div className={cn('transition-opacity duration-500', step >= 1 ? 'opacity-100' : 'opacity-15')}>

// ❌ conditional render — the slide jumps on every press
{step >= 1 ? <Panel /> : null}
```

`engine/diagram.tsx` does this for you via its internal `Reveal`. The one
deliberate exception is a travelling packet (`Link fire`), which is genuinely
transient and mounts for its step only.

## Presenter notes

`notes` is what you *say*, not what is on screen. Pass an array to give each
build step its own line; the drawer follows the step you are on. Write spoken
sentences — contractions are fine, numerals read aloud ("SOC 2 Type One"), no
bullet fragments.

## Diagrams

`engine/diagram.tsx` provides `Stage` (framed card with a caption rail), `Box`,
`Chip`, `Link` (rail + arrowhead + optional travelling packet), `Wall`, `Row`.
Shipped machines: `IsolationDiagram`, `BrokerDiagram`, `ChangeRequestDiagram`,
`PrincipalDiagram`, `LedgerDiagram`.

- The **caption** carries the sentence that changes per step. The slide title
  should not move.
- Connector rails never fully fade — the wiring of a system stays readable even
  where traffic is not flowing yet. Only ghosted nodes drop to `0.12`.
- Color is monochrome plus `kortix-*` tokens for verdicts only: green = allowed
  or merged, orange = held, red = blocked or refused. Never raw Tailwind palette.
- Declare sub-components at **module scope**, never inside a diagram's render —
  a component created during render is a new type every step, so React remounts
  it and the animation restarts instead of continuing. `react-hooks/static-components`
  catches this.

## Structure discipline — the rule that matters most

**Say how many parts there are, then have exactly that many.** The security deck
promised "four answers" and originally ran seven chapters; rebuilding it to four,
with a `Spine` on every chapter slide, did more for it than any visual work.

- Name the chapters in a `const` at the top of the deck file and render them
  through `<Spine chapters={CHAPTERS} active={n} />`.
- One diagram per chapter, one supporting slide at most.
- A fifth idea belongs on the marketing page, not the deck. State the rule in the
  deck's file header so the next edit does not quietly grow one.
- Chapter slides get a title and the machine — **no lead paragraph.** On a build
  slide a second block of prose competes with the narration.
- Roughly 20 seconds of narration per build step. 25–30 builds is a comfortable
  eight-to-ten-minute video.

## Copy accuracy — non-negotiable

Import copy from the marketing content modules
(`apps/web/src/features/marketing/*/content.ts`), never retype it. Those files
carry accuracy gates in their headers: the claims verified against shipped code,
and the corrections that must not be "restored". A deck that retypes a claim is a
deck still saying it a year after the product stopped doing it.

The standing traps (microVM, secret scope, "the key never sits in the sandbox",
who can merge, certifications, license names) now live in
[`claims.md`](../kortix-brand/references/verbal/claims.md), section 3, with the
code each one cites. Read it before you write on-slide text. A claim that is not
in that file is not a claim you may make.

Read [`voice-and-tone.md`](../kortix-brand/references/verbal/voice-and-tone.md)
(section 5.8, deck notes) for voice and the banned phrases.

## Visual language

Values, type, color and the deck column live in
[`layout.md`](../kortix-brand/references/visual/layout.md),
[`typography.md`](../kortix-brand/references/visual/typography.md) and
[`color.md`](../kortix-brand/references/visual/color.md). Do not restate them
here. Compose from `engine/parts.tsx` before you invent chrome: `SectionHead`,
`Panel`, `RowList`, `KortixAsterisk` bullets and real product screenshots in
`Shot`. A deck may set a mono-uppercase eyebrow (decision D4e in
[`decisions.md`](../kortix-brand/references/decisions.md)). App chrome may not.

Theme-following always: semantic tokens and `kortix-*` accents, never a forced
palette and never a `dark:` hack.

A slide is one viewport and must never scroll. Cap screenshots in `vh`:

```tsx
<Shot src="/media/…" alt="…" imgClassName="max-h-[48vh] object-cover object-top" />
```

`object-top` keeps the top of the screen, where the product is, and crops the
empty bottom rather than letterboxing it.

## QA before delivery — do not skip

```bash
cd apps/web
npx tsc --noEmit                    # must be clean in src/app/[locale]/presentations
npx eslint src/app/[locale]/presentations    # 0 errors
```

`tsc --noEmit` reports ~15 pre-existing `@types/bun` `test.each` errors elsewhere
in the app — those are the documented baseline, not yours. Nothing under
`presentations` may appear.

Then drive it in the browser (chrome-devtools MCP):

1. Every slide renders — press `G` for the overview, which shows all slides
   **fully built**, and check it in one screenshot.
2. Step at least one build slide by hand; confirm each press adds exactly one
   stage and the layout does not jump.
3. Both themes. A light screenshot inside a dark deck reads as a bright panel —
   look at it, don't assume.
4. Console clean.

Reload the page before each check: after a hot reload, one press can jump several
steps. That is a dev artifact of accumulated listeners, not a bug in the engine.

Files in this skill

  • SKILL.md11.8 KB
  • evals/evals.json3 KB
  • references/films.md8.4 KB

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…