Skip to content
cccskills
FreePublished on GitHub

add-page

Add a new server-rendered page to the Liftosaur website. Use when creating new public-facing pages with routing, SSR, and client hydration.

The English summary is being prepared. Showing the original description.

astashov/liftosaur761 stars(GitHub 761 + cccskills 0)
Works withAWS

Run score Not tested

See how to install

Files included (1)

  • SKILL.md5.7 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Add New Page to Liftosaur Website

Create a new server-rendered page at the specified route. Requirements: $ARGUMENTS

Research Phase

Before creating files, read these existing examples to understand the patterns:

  1. Read src/pages/allExercises/allExercisesHtml.tsx — simple HTML wrapper example
  2. Read src/pages/allExercises/allExercisesContent.tsx — simple content component
  3. Read src/allExercises.tsx — client entry hydration
  4. Read lambda/allExercises.tsx — lambda render function
  5. Check lambda/index.ts for the route registration pattern (search for getAllExercisesEndpoint)
  6. Check webpack.config.js for the entry point pattern

Architecture: 4-Layer Pattern

Every public page uses 4 layers. For a page at /myroute with entry name mypage:

Layer 1: Lambda Render (lambda/myPage.tsx)

import { h } from "preact";
import { IAccount } from "../src/models/account";
import { MyPageHtml } from "../src/pages/myPage/myPageHtml";
import { renderPage } from "./render";

export function renderMyPageHtml(client: Window["fetch"], account?: IAccount): string {
  return renderPage(<MyPageHtml client={client} account={account} />);
}

Layer 2: HTML Component (src/pages/myPage/myPageHtml.tsx)

Wraps content in <Page>. Key props:

  • css={["mypage"]} / js={["mypage"]} — must match webpack entry key
  • maxWidth={1200} — page max width
  • title, canonical, description, ogUrl — SEO metadata
  • account — optional user account for nav state
  • url="/myroute" — highlights nav item in TopNavMenu
  • data={data} — serialized for hydration (exclude client from data!)
import { h, JSX } from "preact";
import { Page } from "../../components/page";
import { IAccount } from "../../models/account";
import { MyPageContent } from "./myPageContent";

interface IProps {
  client: Window["fetch"];
  account?: IAccount;
}

export function MyPageHtml(props: IProps): JSX.Element {
  const { client, ...data } = props;
  return (
    <Page
      css={["mypage"]} js={["mypage"]} maxWidth={1200}
      title="Page Title | Liftosaur"
      canonical="https://www.liftosaur.com/myroute"
      description="Page description"
      ogUrl="https://www.liftosaur.com/myroute"
      account={props.account} data={data} client={client} url="/myroute"
    >
      <MyPageContent client={client} {...data} />
    </Page>
  );
}

Layer 3: Content Component (src/pages/myPage/myPageContent.tsx)

The interactive Preact component. Export the props interface (needed by Layer 4).

Layer 4: Client Entry (src/myPage.tsx)

import { h } from "preact";
import { PageWrapper } from "./components/pageWrapper";
import { IMyPageContentProps, MyPageContent } from "./pages/myPage/myPageContent";
import { HydrateUtils } from "./utils/hydrate";

function main(): void {
  HydrateUtils.hydratePage<IMyPageContentProps>((pageWrapperProps, data) => (
    <PageWrapper {...pageWrapperProps}>
      <MyPageContent {...data} client={window.fetch.bind(window)} />
    </PageWrapper>
  ));
}
main();

Wiring Steps

1. Webpack entry (webpack.config.js)

Add to entry in mainConfig:

mypage: ["./src/myPage.tsx", "./src/index.css"],

2. Lambda route (lambda/index.ts)

Add import:

import { renderMyPageHtml } from "./myPage";

Add endpoint + handler (near similar endpoints):

const getMyPageEndpoint = Endpoint.build("/myroute");
const getMyPageHandler: RouteHandler<IPayload, APIGatewayProxyResult, typeof getMyPageEndpoint> = async ({ payload }) => {
  const { di } = payload;
  let account: IAccount | undefined;
  const userResult = await getUserAccount(payload, { withPrograms: true });
  if (userResult.success) {
    account = userResult.data.account;
  }
  return {
    statusCode: 200,
    body: renderMyPageHtml(di.fetch, account),
    headers: { "content-type": "text/html" },
  };
};

Register in router chain (~line 2700):

.get(getMyPageEndpoint, getMyPageHandler)

3. Redirects (_redirects and _redirects_staging)

Add redirect entries so the CDN (Netlify) proxies the route to the Lambda backend:

In _redirects:

/myroute https://api3.liftosaur.com/myroute  200

In _redirects_staging:

/myroute https://api3-dev.liftosaur.com/myroute  200

Place the entry before any parameterized routes for the same prefix (e.g. /myroute before /myroute/:id).

4. TopNavMenu (src/components/topNavMenu.tsx)

If page should appear in nav, update getMenuItems().

Reusable Components

  • TopNavMenu + FooterPage: Auto-included via PageWrapper (don't set nowrapper on <Page>)
  • ScrollableTabs (src/components/scrollableTabs.tsx): Tabbed content
  • SelectLink (src/components/selectLink.tsx): Dropdown link with bottom sheet
  • ExerciseImage (src/components/exerciseImage.tsx): Exercise illustrations
  • Markdown (src/components/markdown.tsx): Renders markdown
  • Icons: src/components/icons/
  • builtinProgramProperties (src/models/builtinPrograms.ts): Metadata for builtin programs
  • ProgramDao (lambda/dao/programDao.ts): Fetches program data from CDN

Key Rules

  • The data prop on <Page> is serialized into a hidden #data div. Keep it lightweight.
  • Settings.build() creates default settings for public pages without user context.
  • Split complex content into separate component files within the page directory.
  • Run npx tsc --noEmit after creating all files to verify no TypeScript errors.

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

Add a new built-in exercise to Liftosaur end-to-end — image (gymvisual.com → S3), catalog registration, description page, and the LLM/MCP exercise list. Use when adding one or more built-in exercises to the app catalog.

The English summary is being prepared. Showing the original description.

astashov/liftosaur761Updated Oct 26, 2026

Create a 30%-off coupon for an affiliate or creator on iOS and Android, and draft the reply that tells them how to use it. Use when asked to create, set up or send a coupon or promo code for an affiliate, influencer or creator.

The English summary is being prepared. Showing the original description.

astashov/liftosaur761Updated Oct 26, 2026

Interactively investigate a production Rollbar error. Accepts an occurrence id, item id, item counter, or Rollbar URL. Fetches the error data, assesses how widespread it is, drills into affected users (events, logs, storage, captured state), and works with the developer toward a fix or ignore decision. Use for collaborative debugging; use /fix-rollbar for a straight fetch-and-fix of a single occurrence.

The English summary is being prepared. Showing the original description.

astashov/liftosaur761Updated Oct 26, 2026

Investigate a user-reported issue using server logs, user events, storage snapshots, and Rollbar. Use when given a user id or email plus an issue description (e.g. from a support email).

The English summary is being prepared. Showing the original description.

astashov/liftosaur761Updated Oct 26, 2026

Build a feature end to end — decide worktree vs here, design the architecture and debate it with codex, get the user's approval on a one-pass-readable plan, state the API/state contract, implement with visible diffs, then run two codex reviews (bugs + architecture) and document the result. Use when asked to build/implement/add a feature or do a refactor that involves design decisions.

The English summary is being prepared. Showing the original description.

astashov/liftosaur761Updated Oct 26, 2026

Fix a production exception from Rollbar in an interactive Claude Code session. Fetches the occurrence data, analyzes the error, and either implements a fix or adds the error to the ignore list. Use when given a Rollbar occurrence ID.

The English summary is being prepared. Showing the original description.

astashov/liftosaur761Updated Oct 26, 2026

See all skills by astashov

Report a problem with this skill