SuperAPI AI Directory

kortix-presentation

Office & productivity

View repository

Updated 10-30 01:12

About this skill

Kortix Presentations

Load kortix-brand 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).

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():

'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:

{
  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

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.

// ✅ 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, 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 (section 5.8, deck notes) for voice and the banned phrases.

Visual language

Values, type, color and the deck column live in layout.md, typography.md and 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). 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:

<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

pnpm --filter Kortix-Computer-Frontend typecheck   # tsgo; must be clean in src/app/[locale]/presentations
cd apps/web && npx eslint src/app/[locale]/presentations    # 0 errors

Never run raw tsc --noEmit in apps/web: on 2026-10-21 it ran 817 s at 3.18 GB and did not finish. Errors elsewhere in the app are the baseline (compare against origin/dev), 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.