Skip to content

Instantly share code, notes, and snippets.

@ndelangen
Created July 22, 2026 22:40
Show Gist options
  • Select an option

  • Save ndelangen/df0ead23408334488744ae30e1fa8186 to your computer and use it in GitHub Desktop.

Select an option

Save ndelangen/df0ead23408334488744ae30e1fa8186 to your computer and use it in GitHub Desktop.
Dune Zone faction authoring composition prototypes

Faction authoring composition prototypes

Three structurally different concepts compared for Dune Zone's shared Create/Edit faction experience:

  • tabbed-studio.svg — a familiar domain-tabbed studio with a persistent artifact rail.
  • artifact-canvas.svg — an artifact-first canvas where selecting output elements reveals their responsible fields.
  • facet-chapters.svg — one continuous document of domain chapters with local artifact previews.

Selected direction

Facet Chapters was selected as the foundation.

The editor becomes one continuously scrolling document organized as:

  1. Identity & Appearance
  2. Leaders
  3. Alliance
  4. Forces & Worlds
  5. Rules & Advantages

Desktop and tablet use a compact chapter map plus local previews beside the fields that affect them. Mobile removes the chapter rail and artifact previews and renders the same controls vertically in chapter order. A sticky route toolbar owns explicit save and publish state. Ordered collections retain pointer and keyboard reordering. Every FactionInputSchema field is editable except the intentional extras[] exception, whose existing value is preserved unchanged.

The static mockups are discussion artifacts, not implementation code.

Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="1024" viewBox="0 0 1440 1024" role="img" aria-labelledby="title desc">
<title id="title">Faction Studio — domain-tabbed authoring concept</title>
<desc id="desc">A desktop faction editor with sticky save toolbar, domain tabs, an Identity and Appearance form, a visual background pipeline, and artifact-aware previews.</desc>
<defs>
<linearGradient id="appBg" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#f5f1e8"/>
<stop offset="1" stop-color="#ebe4d6"/>
</linearGradient>
<linearGradient id="sand" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#f1c27d"/>
<stop offset="1" stop-color="#a3482f"/>
</linearGradient>
<radialGradient id="patternColor" cx="35%" cy="30%" r="85%">
<stop offset="0" stop-color="#f7d999"/>
<stop offset="0.58" stop-color="#c65d3c"/>
<stop offset="1" stop-color="#57283e"/>
</radialGradient>
<pattern id="waves" width="34" height="28" patternUnits="userSpaceOnUse">
<path d="M-8 18 Q9 1 26 18 T60 18" fill="none" stroke="#fbe7b5" stroke-width="4" opacity=".78"/>
<path d="M-8 27 Q9 10 26 27 T60 27" fill="none" stroke="#5b2941" stroke-width="3" opacity=".65"/>
</pattern>
<pattern id="miniWaves" width="16" height="13" patternUnits="userSpaceOnUse">
<path d="M-4 9 Q4 1 12 9 T28 9" fill="none" stroke="#5d3044" stroke-width="2.2" opacity=".75"/>
</pattern>
<filter id="shadow" x="-20%" y="-20%" width="140%" height="150%">
<feDropShadow dx="0" dy="3" stdDeviation="7" flood-color="#3e3024" flood-opacity=".12"/>
</filter>
<filter id="softShadow" x="-20%" y="-20%" width="140%" height="150%">
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#3e3024" flood-opacity=".10"/>
</filter>
<clipPath id="tokenClip"><circle cx="1179" cy="453" r="72"/></clipPath>
</defs>
<rect width="1440" height="1024" fill="url(#appBg)"/>
<!-- Persistent app chrome -->
<rect width="1440" height="58" fill="#2e2730"/>
<text x="32" y="37" fill="#f6ead2" font-family="Inter, ui-sans-serif, system-ui" font-size="20" font-weight="800" letter-spacing="1.3">DUNE ZONE</text>
<text x="198" y="36" fill="#cfc3b6" font-family="Inter, ui-sans-serif, system-ui" font-size="14">Factions</text>
<text x="282" y="36" fill="#cfc3b6" font-family="Inter, ui-sans-serif, system-ui" font-size="14">Rulesets</text>
<circle cx="1381" cy="29" r="16" fill="#a95437"/>
<text x="1381" y="34" text-anchor="middle" fill="#fff8ec" font-family="Inter, ui-sans-serif, system-ui" font-size="12" font-weight="700">ND</text>
<!-- Route header -->
<rect x="0" y="58" width="1440" height="82" fill="#fffaf1" stroke="#d8cdbd"/>
<text x="30" y="89" fill="#766b60" font-family="Inter, ui-sans-serif, system-ui" font-size="12" font-weight="700">FACTIONS / EDIT</text>
<text x="30" y="121" fill="#2d2826" font-family="Inter, ui-sans-serif, system-ui" font-size="28" font-weight="760">Faction Studio</text>
<rect x="214" y="97" width="155" height="27" rx="13.5" fill="#f1e8d9"/>
<circle cx="232" cy="110.5" r="4.5" fill="#a95437"/>
<text x="244" y="115" fill="#685d53" font-family="Inter, ui-sans-serif, system-ui" font-size="12" font-weight="650">3 unsaved changes</text>
<!-- Sticky route toolbar -->
<rect x="0" y="140" width="1440" height="62" fill="#fffdf8" stroke="#d9cfc2" filter="url(#softShadow)"/>
<g font-family="Inter, ui-sans-serif, system-ui">
<rect x="30" y="153" width="116" height="36" rx="7" fill="#f4eee4" stroke="#cfc2b2"/>
<text x="88" y="176" text-anchor="middle" fill="#3e3832" font-size="13" font-weight="650">Load faction</text>
<rect x="156" y="153" width="90" height="36" rx="7" fill="#f4eee4" stroke="#cfc2b2"/>
<text x="201" y="176" text-anchor="middle" fill="#3e3832" font-size="13" font-weight="650">Reset</text>
<text x="932" y="166" fill="#766b60" font-size="11" font-weight="700">LAST PUBLISHED</text>
<text x="932" y="183" fill="#3d3732" font-size="13">22 Jul 2026, 18:42</text>
<rect x="1082" y="153" width="136" height="36" rx="7" fill="#f4eee4" stroke="#cfc2b2"/>
<text x="1150" y="176" text-anchor="middle" fill="#3e3832" font-size="13" font-weight="650">Close editor</text>
<rect x="1228" y="153" width="182" height="36" rx="7" fill="#9d492f"/>
<text x="1319" y="176" text-anchor="middle" fill="#fff9ef" font-size="13" font-weight="760">Save &amp; schedule publish</text>
</g>
<!-- Main content container -->
<g transform="translate(26 224)" font-family="Inter, ui-sans-serif, system-ui">
<!-- Domain tab rail -->
<rect width="224" height="762" rx="12" fill="#fffaf2" stroke="#d6c9b9" filter="url(#softShadow)"/>
<text x="18" y="32" fill="#807467" font-size="11" font-weight="760" letter-spacing=".8">FACTION PARTS</text>
<rect x="10" y="47" width="204" height="63" rx="8" fill="#4b3b48"/>
<circle cx="31" cy="70" r="8" fill="#e1a962"/>
<path d="M27 70h8M31 66v8" stroke="#4b3b48" stroke-width="2"/>
<text x="49" y="72" fill="#fff9ef" font-size="13" font-weight="760">Identity &amp; Appearance</text>
<text x="49" y="92" fill="#d9ced4" font-size="11">Name, mark, colors, pattern</text>
<g fill="#342f2b" font-size="13" font-weight="690">
<text x="49" y="146">Leaders</text>
<text x="49" y="205">Alliance</text>
<text x="49" y="264">Troops</text>
<text x="49" y="323">Rules &amp; setup</text>
<text x="49" y="382">Planets</text>
</g>
<g fill="#867b70" font-size="11">
<text x="49" y="163">Faction leader + supporting cast</text>
<text x="49" y="222">Ability + ordered decals</text>
<text x="49" y="281">Fronts, backs, counts, home</text>
<text x="49" y="340">Setup, fate, advantages</text>
<text x="49" y="399">Artwork and descriptions</text>
</g>
<g fill="none" stroke="#cabdaf">
<line x1="18" x2="206" y1="122" y2="122"/><line x1="18" x2="206" y1="181" y2="181"/>
<line x1="18" x2="206" y1="240" y2="240"/><line x1="18" x2="206" y1="299" y2="299"/>
<line x1="18" x2="206" y1="358" y2="358"/><line x1="18" x2="206" y1="417" y2="417"/>
</g>
<g>
<circle cx="31" cy="141" r="9" fill="#d6ead8"/><path d="M27 141l3 3 5-6" fill="none" stroke="#3b7546" stroke-width="1.8"/>
<circle cx="31" cy="200" r="9" fill="#d6ead8"/><path d="M27 200l3 3 5-6" fill="none" stroke="#3b7546" stroke-width="1.8"/>
<circle cx="31" cy="259" r="9" fill="#f4d9cf"/><text x="31" y="263" text-anchor="middle" fill="#9d492f" font-size="10" font-weight="800">2</text>
<circle cx="31" cy="318" r="9" fill="#f4e9c9"/><text x="31" y="322" text-anchor="middle" fill="#86671b" font-size="10" font-weight="800">1</text>
<circle cx="31" cy="377" r="9" fill="#d6ead8"/><path d="M27 377l3 3 5-6" fill="none" stroke="#3b7546" stroke-width="1.8"/>
</g>
<rect x="14" y="688" width="196" height="58" rx="8" fill="#f4eee5"/>
<text x="28" y="710" fill="#5f554d" font-size="11" font-weight="700">Schema coverage</text>
<text x="28" y="729" fill="#7c7167" font-size="11">34 / 34 editable fields covered</text>
<text x="28" y="743" fill="#96897e" font-size="9.5">Extras preserved intentionally</text>
<!-- Active form -->
<g transform="translate(244 0)">
<text x="0" y="29" fill="#2d2826" font-size="24" font-weight="780">Identity &amp; Appearance</text>
<text x="0" y="50" fill="#756a61" font-size="12.5">Build the visual system reused across cards, tokens, shields and sheets.</text>
<rect x="0" y="67" width="612" height="129" rx="10" fill="#fffaf3" stroke="#d6c9b9"/>
<text x="18" y="91" fill="#3c3530" font-size="14" font-weight="760">Faction identity</text>
<text x="18" y="115" fill="#756a61" font-size="10.5" font-weight="700">NAME</text>
<rect x="18" y="124" width="274" height="42" rx="7" fill="#fff" stroke="#bfae9c"/>
<text x="31" y="150" fill="#302b28" font-size="13">House Ecaz</text>
<text x="310" y="115" fill="#756a61" font-size="10.5" font-weight="700">FACTION MARK</text>
<rect x="310" y="124" width="178" height="42" rx="7" fill="#fff" stroke="#bfae9c"/>
<text x="323" y="150" fill="#302b28" font-size="13">ecaz</text><path d="M467 141l6 6 6-6" fill="none" stroke="#766b60" stroke-width="1.6"/>
<text x="506" y="115" fill="#756a61" font-size="10.5" font-weight="700">SHEET COLOR</text>
<rect x="506" y="124" width="88" height="42" rx="7" fill="#fff" stroke="#bfae9c"/>
<circle cx="525" cy="145" r="10" fill="#9e4a5d"/><text x="541" y="149" fill="#302b28" font-size="11">#9e4a5d</text>
<text x="18" y="185" fill="#85786e" font-size="10.5">Also choose the closest Tabletop Simulator seat colors.</text>
<!-- Background builder -->
<rect x="0" y="210" width="612" height="506" rx="10" fill="#fffaf3" stroke="#c7b49f" filter="url(#softShadow)"/>
<text x="18" y="239" fill="#3c3530" font-size="15" font-weight="780">Background builder</text>
<rect x="455" y="222" width="139" height="26" rx="13" fill="#efe5d7"/>
<text x="524.5" y="239" text-anchor="middle" fill="#655a51" font-size="10.5" font-weight="700">Used by 6 artifacts</text>
<text x="18" y="260" fill="#756a61" font-size="11.5">See exactly how the pattern becomes your faction’s visual signature.</text>
<!-- Pipeline connector -->
<line x1="83" x2="526" y1="296" y2="296" stroke="#bca894" stroke-width="2"/>
<g fill="#fffaf3" stroke="#9b8068" stroke-width="2">
<circle cx="83" cy="296" r="8"/><circle cx="231" cy="296" r="8"/><circle cx="379" cy="296" r="8"/><circle cx="526" cy="296" r="8"/>
</g>
<g fill="#715f50" font-size="10.5" font-weight="720" text-anchor="middle">
<text x="83" y="281">1 · PATTERN</text><text x="231" y="281">2 · TREATMENT</text><text x="379" y="281">3 · COLORS</text><text x="526" y="281">4 · COMPOSITE</text>
</g>
<!-- Step cards -->
<rect x="18" y="315" width="132" height="151" rx="8" fill="#f7f0e6" stroke="#d6c6b4"/>
<rect x="29" y="327" width="110" height="70" rx="6" fill="#ead7b9"/>
<rect x="29" y="327" width="110" height="70" rx="6" fill="url(#miniWaves)"/>
<text x="29" y="417" fill="#655a51" font-size="10" font-weight="700">PATTERN ASSET</text>
<text x="29" y="438" fill="#332e2b" font-size="11.5">ecaz-waves</text>
<path d="M119 431l5 5 5-5" fill="none" stroke="#766b60" stroke-width="1.4"/>
<text x="29" y="455" fill="#8b7e72" font-size="9.5">Change texture</text>
<rect x="166" y="315" width="132" height="151" rx="8" fill="#f7f0e6" stroke="#d6c6b4"/>
<text x="178" y="338" fill="#655a51" font-size="10" font-weight="700">PATTERN MODE</text>
<rect x="178" y="348" width="108" height="30" rx="15" fill="#e4d7c8"/>
<rect x="231" y="350" width="53" height="26" rx="13" fill="#4b3b48"/>
<text x="204" y="368" text-anchor="middle" fill="#6e6259" font-size="10">Normal</text>
<text x="257" y="368" text-anchor="middle" fill="#fff8ed" font-size="10" font-weight="700">Invert</text>
<text x="178" y="403" fill="#655a51" font-size="9.5" font-weight="700">DEFINITION 68%</text>
<line x1="178" x2="286" y1="417" y2="417" stroke="#cab9a6" stroke-width="4" stroke-linecap="round"/>
<line x1="178" x2="251" y1="417" y2="417" stroke="#9d492f" stroke-width="4" stroke-linecap="round"/>
<circle cx="251" cy="417" r="6" fill="#fff" stroke="#9d492f" stroke-width="2"/>
<text x="178" y="444" fill="#655a51" font-size="9.5" font-weight="700">INFLUENCE 42%</text>
<line x1="178" x2="286" y1="457" y2="457" stroke="#cab9a6" stroke-width="4" stroke-linecap="round"/>
<line x1="178" x2="223" y1="457" y2="457" stroke="#9d492f" stroke-width="4" stroke-linecap="round"/>
<circle cx="223" cy="457" r="6" fill="#fff" stroke="#9d492f" stroke-width="2"/>
<rect x="314" y="315" width="132" height="151" rx="8" fill="#f7f0e6" stroke="#d6c6b4"/>
<text x="326" y="338" fill="#655a51" font-size="10" font-weight="700">BASE COLOR</text>
<rect x="326" y="348" width="108" height="34" rx="6" fill="url(#sand)"/>
<text x="326" y="402" fill="#655a51" font-size="10" font-weight="700">PATTERN COLOR</text>
<rect x="326" y="412" width="108" height="34" rx="6" fill="url(#patternColor)"/>
<text x="326" y="459" fill="#8b7e72" font-size="9.5">Solid or gradient</text>
<rect x="462" y="315" width="132" height="151" rx="8" fill="#6a2f42" stroke="#d6c6b4"/>
<rect x="462" y="315" width="132" height="151" rx="8" fill="url(#patternColor)"/>
<rect x="462" y="315" width="132" height="151" rx="8" fill="url(#waves)" opacity=".9"/>
<rect x="474" y="430" width="108" height="24" rx="12" fill="#fff7e9" fill-opacity=".9"/>
<text x="528" y="446" text-anchor="middle" fill="#4b3b48" font-size="9.5" font-weight="800">LIVE COMPOSITE</text>
<!-- Expanded controls -->
<rect x="18" y="485" width="576" height="174" rx="8" fill="#f7f0e6" stroke="#d6c6b4"/>
<text x="32" y="510" fill="#514840" font-size="12" font-weight="760">Color layers</text>
<text x="32" y="529" fill="#82756b" font-size="10.5">Base fills the asset. Pattern color is revealed through the treated mask.</text>
<rect x="32" y="545" width="260" height="89" rx="7" fill="#fffaf3" stroke="#d3c3b1"/>
<text x="44" y="566" fill="#655a51" font-size="9.5" font-weight="750">BASE · LINEAR GRADIENT</text>
<rect x="44" y="577" width="149" height="30" rx="5" fill="url(#sand)"/>
<text x="206" y="596" fill="#544b44" font-size="10.5">45°</text>
<text x="44" y="624" fill="#85786e" font-size="9.5">2 stops · drag to reorder</text>
<rect x="306" y="545" width="274" height="89" rx="7" fill="#fffaf3" stroke="#d3c3b1"/>
<text x="318" y="566" fill="#655a51" font-size="9.5" font-weight="750">PATTERN · RADIAL GRADIENT</text>
<rect x="318" y="577" width="149" height="30" rx="5" fill="url(#patternColor)"/>
<text x="480" y="596" fill="#544b44" font-size="10.5">x 35 · y 30</text>
<text x="318" y="624" fill="#85786e" font-size="9.5">3 stops · radius 85</text>
<rect x="32" y="669" width="562" height="32" rx="6" fill="#efe7da"/>
<text x="44" y="690" fill="#6e6259" font-size="10.5">Advanced: scale 100% · rotation 0° · position centered</text>
<path d="M568 682l6 6 6-6" fill="none" stroke="#766b60" stroke-width="1.4"/>
</g>
<!-- Artifact-aware preview rail -->
<g transform="translate(876 0)">
<rect width="512" height="762" rx="12" fill="#fffaf2" stroke="#d6c9b9" filter="url(#softShadow)"/>
<text x="20" y="31" fill="#2f2a27" font-size="16" font-weight="780">Where this appears</text>
<text x="20" y="51" fill="#776c62" font-size="11.5">Preview the artifact that matters for this field.</text>
<rect x="18" y="66" width="476" height="39" rx="7" fill="#eee5d9"/>
<rect x="21" y="69" width="83" height="33" rx="6" fill="#4b3b48"/>
<text x="62.5" y="90" text-anchor="middle" fill="#fff8ed" font-size="10.5" font-weight="750">Token</text>
<text x="143" y="90" text-anchor="middle" fill="#655a51" font-size="10.5">Shield</text>
<text x="224" y="90" text-anchor="middle" fill="#655a51" font-size="10.5">Leader</text>
<text x="308" y="90" text-anchor="middle" fill="#655a51" font-size="10.5">Alliance</text>
<text x="402" y="90" text-anchor="middle" fill="#655a51" font-size="10.5">Sheet</text>
<text x="471" y="90" text-anchor="middle" fill="#655a51" font-size="10.5">All</text>
<rect x="18" y="120" width="476" height="418" rx="9" fill="#e9e0d3"/>
<circle cx="256" cy="279" r="128" fill="#f8f0e2" stroke="#9a836e" stroke-width="7"/>
<circle cx="256" cy="279" r="116" fill="url(#patternColor)"/>
<circle cx="256" cy="279" r="116" fill="url(#waves)" opacity=".9"/>
<path d="M217 296c4-41 28-76 39-91 11 15 35 50 39 91-12-9-24-13-39-13s-27 4-39 13zm17-50c-14 8-28 20-40 35 18-3 34 0 48 8m36 0c14-8 30-11 48-8-12-15-26-27-40-35" fill="none" stroke="#fff0c4" stroke-width="9" stroke-linecap="round" stroke-linejoin="round"/>
<rect x="134" y="432" width="244" height="54" rx="27" fill="#fffaf1" fill-opacity=".92"/>
<text x="256" y="455" text-anchor="middle" fill="#4a3a43" font-size="12" font-weight="800" letter-spacing="1.1">HOUSE ECAZ</text>
<text x="256" y="474" text-anchor="middle" fill="#75675f" font-size="10.5">Faction token · actual renderer</text>
<text x="30" y="521" fill="#887b70" font-size="10">Preview is scaled to fit; output renderer remains isolated.</text>
<text x="20" y="572" fill="#514840" font-size="12" font-weight="760">Affected artifacts</text>
<g font-size="10.5" font-weight="690">
<rect x="20" y="587" width="72" height="27" rx="13.5" fill="#ead9bd"/><text x="56" y="605" text-anchor="middle" fill="#624e3b">Token</text>
<rect x="100" y="587" width="74" height="27" rx="13.5" fill="#ead9bd"/><text x="137" y="605" text-anchor="middle" fill="#624e3b">Shield</text>
<rect x="182" y="587" width="78" height="27" rx="13.5" fill="#ead9bd"/><text x="221" y="605" text-anchor="middle" fill="#624e3b">Leaders</text>
<rect x="268" y="587" width="77" height="27" rx="13.5" fill="#ead9bd"/><text x="306.5" y="605" text-anchor="middle" fill="#624e3b">Troops</text>
<rect x="353" y="587" width="92" height="27" rx="13.5" fill="#ead9bd"/><text x="399" y="605" text-anchor="middle" fill="#624e3b">Alliance</text>
<rect x="20" y="622" width="119" height="27" rx="13.5" fill="#ead9bd"/><text x="79.5" y="640" text-anchor="middle" fill="#624e3b">Catalogue cards</text>
</g>
<rect x="18" y="670" width="476" height="72" rx="8" fill="#f1eadf"/>
<circle cx="42" cy="695" r="11" fill="#dce9d8"/><path d="M37 695l4 4 7-8" fill="none" stroke="#4f7a48" stroke-width="2"/>
<text x="62" y="696" fill="#4c443e" font-size="11" font-weight="730">Preview matches the saved field values</text>
<text x="62" y="716" fill="#7b7066" font-size="10.5">Errors pause only the affected artifact and explain why.</text>
<text x="20" y="756" fill="#91857a" font-size="10">Desktop/tablet preview only · mobile is a single vertical form</text>
</g>
</g>
</svg>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment