A blueprint and tablet show a coded textiles folio becoming an editable Canva design.A blueprint and tablet show a coded textiles folio becoming an editable Canva design.

Most HSC (NSW Higher School Certificate) students design their major work folio in Canva or in an Adobe app. It’s free through school and it’s what they know. The students I asked hadn’t considered code at all. They reckoned it was too abstract for design work.

The hsc-major-work-example repository gets you both. Lay the folio out in code, where page limits and type rules are easy to check, then carry it into Canva and keep designing there.

Warning

The folio in the repository, “The Raven’s Ledger”, is fictional. The student is invented, the plates were generated by AI, and every number was made up. It shows a workflow, not work to copy. A real folio has to be the student’s own work, with any AI help acknowledged the way NESA and the school require. Responsible AI and the HSC covers where that line sits.

Why start in code

Each A3 page is a block in one HTML file, styled by a shared stylesheet. That’s a bit of setup for a textiles folio, but it gives you things a blank Canva page does not:

  • The structure is fixed early. Twelve A3 pages in NESA’s section order: 2 for Design Inspiration, 3 for Visual Design Development, 3 for Manufacturing Specification, and 4 for Investigation, Experimentation and Evaluation. Anything past a section’s limit is not marked, so it pays to see the limits from day one.
  • The rules are written down once. Type at 12 point or larger, colours and fonts from one brand kit, one form for cross-references. A rule in the stylesheet applies to every page, so you don’t hand-adjust twelve tables in Canva.
  • Changes are tracked. Git records every change, so you can see what moved between drafts.
  • It prints true to size. A browser print at 100 per cent gives a real A3 proof, with true millimetres for checking scales and swatches.

This is the caption rule from the deck’s stylesheet. Every plate caption on pages 1 and 2 uses it, so nobody has to remember the 12 point floor caption by caption:

.caption-row {
  display: flex;
  align-items: flex-start;
  gap: 3mm;
  margin-top: 2.5mm;
  font-family: "PT Serif", Times, serif;
  font-size: 12pt;
  line-height: 1.3;
  color: #1a1a1a;
}

You don’t have to write the code yourself. Agents are good at turning a described change into code. Ask for the change, watch the preview update, and check the result on the page. Acknowledge that help like any other AI assistance.

What the Canva Sync agent does

Canva’s importer can’t read a real stylesheet. So the Canva Sync agent carries the deck across in stages, one way, and stops at the first stage that fails:

  1. Doctor checks that everything the pipeline needs is installed.
  2. Build flattens the deck into plain boxes with their styles written inline, the shape Canva reads.
  3. Verify renders every page from the deck and from the flattened copy, and compares them pixel for pixel. A page fails if more than 0.35 per cent of its pixels differ. The worst of the twelve example pages is about 0.15 per cent.
  4. Extract measures every shape, image, and text block into an element list.
  5. Probe checks that the Canva connector offers the operations the push needs.
  6. Push sends the pages to Canva, page by page.
  7. Check reads the design back from Canva and compares it with the repository.

Here is the first caption on page 1 going through Build. The source is short, because the stylesheet does the work:

<div class="caption-row">
  <span class="plate-num">1</span>
  <span><span style="font-style:italic">Wednesday</span>, the school dance dress, my rendered study ...</span>
</div>

The flattened copy carries every style inline (trimmed here):

<div
  style="display:flex;align-items:flex-start;gap:3mm;margin-top:2.5mm;font-family:'PT Serif', Times, serif;font-size:12pt;line-height:1.3;color:#1A1A1A"
>
  <span style="flex:0 0 7mm;height:7mm;border-radius:50%;background:#C8C9D6;font-size:13pt;...">1</span>
  <span><span style="font-style:italic">Wednesday</span>, the school dance dress, my rendered study ...</span>
</div>

Extract then turns that caption into one element with a position and a style. Canva’s A3 page is 1123 by 1588 pixels, so 12 point lands as 16 pixels:

{
  "kind": "text",
  "x": 132.3,
  "y": 738.2,
  "w": 191.0,
  "h": 145.6,
  "text": "Wednesday, the school dance dress, my rendered study ...",
  "size": 16,
  "family": "PT Serif",
  "lh": 1.3,
  "color": "#1A1A1A"
}
The Verify step's render of a folio page beside its pixel diff, with the few differing pixels highlighted.The Verify step's render of a folio page beside its pixel diff, with the few differing pixels highlighted.

Page 12 is the one that doesn’t pass cleanly. Its evaluation text runs across two columns with text-wrap: pretty, which picks line breaks by looking ahead to the end of the paragraph. Flattening splits that paragraph at the column break, so Chrome breaks one word differently. Same words, same column, same height. Rather than loosen the tolerance for every page, page 12 gets a known residual of 0.5 per cent in canva.config.json, with the reason written down in the docs.

Ask your agent host to sync, with page labels (page 01, 03), all, or check:

sync to Canva 01 03

With no argument it prepares everything and asks what to push.

The operations the push sends by default are not in Canva’s public MCP documentation, so Probe checks the connector’s tool list before anything is sent, and the first page goes across one chunk at a time and is read back. Canva’s documented public connector can replace text in an existing design but can’t create page elements.

What crosses and what does not

Page 1 of the folio in the browser preview on the left and the same page in Canva on the right.Page 1 of the folio in the browser preview on the left and the same page in Canva on the right.

Positions, sizes, layering, colours, text, weights, italics, alignment, and mapped images all cross. Three things don’t:

  • Fonts. The connector’s format operation has no font family setting, so text lands in Canva’s default face at the right size and weight. Apply Fraunces and PT Serif from the brand kit in Canva.
  • Hatched fills on the pattern-piece plates flatten to their base tone.
  • Images have to be uploaded to Canva first, with their ids recorded in canva.local.json. The map is keyed by filename, so a renamed image quietly goes stale. Anything unmapped arrives as a placeholder rectangle to fill in Canva.

Two ways to work

Keep the repository in charge. Edit the deck in code, push to Canva, and use Canva to view, share, and comment. A change made in Canva is reported as a difference for you to decide about. It is never written back into the deck.

Hand over to Canva. Push once, then have a crack at the design in Canva. From that point your Canva design is the folio, and the repository is the scaffold you started from. The sync does not pull Canva edits back, and it is not meant to. check still shows how far the design has moved from where it started, if you want to know.

Most students might like the second way better. That’s fine. The structure, page limits, and type sizes travel into Canva with you. Keeping them as the design changes is up to you. The NESA Assessor agent will still work with a PDF directly from Canva.

Marking the Canva version

The NESA Assessor marks a PDF, and it does not care which tool made it. Export the folio from Canva as an A3 PDF and give the assessor its path:

Mark the folio at output/my-folio-from-canva.pdf

The assessor measures that file the same way it measures any other: page count and size, the smallest text on every page, image and swatch sizes, scales, and totals. Then it marks each section against the NESA descriptors. Designing freely in Canva makes those checks more useful, not less. It’s easy to shrink a caption below 12 point or push a section onto a thirteenth page while moving things around.

Before you print for submission, check the Canva PDF at 100 per cent. If a drawing states a scale or a swatch claims a size, measure it on the printed page.

Without a connector

Every step up to the push still runs, and the agent says the push needs a connector. build --pdf also writes a true-size A3 PDF of the twelve pages, about 100 MB because the plates are embedded at print resolution. Canva can upload it, but the result is harder to edit. The Canva sync docs cover the other routes.

Anticipating change

Both Claude and Canva evolve by the week these days. If the sync fails, ask your agent to fix it. The sync code is all in the repository and adjusting code is where agents really shine. If it still does not work, open a issue on the repository for help.

What you need

  • An editor, such as VS Code, for writing and managing your content.
  • An agent host, for example Claude Code, GitHub Copilot, or Codex.
  • Python 3.12 or newer, with the packages in requirements.txt.
  • Chrome or Edge, for rendering pages during Verify.
  • A Canva connector for the push. I.e. for Claude, add the Canva web connector in the Claude settings.
  • canva.config.json at the repository root, and a local canva.local.json with your Canva ids.

Run Doctor first. It names whatever is missing:

python .agents/skills/canva-sync/scripts/canva_sync.py doctor