Category: Packs

This category contains downloadable Packs, each made up of three sequential CSS 3D Components. Packs are designed as structured building blocks that can be assembled into larger Projects, where multiple Packs appear in a defined sequence. As one type of project format on the site, Packs provide a clear, modular way to group related Components into cohesive sets.

  • 7 ShapeCalc: unit-less values with calc()

    7 ShapeCalc: unit-less values with calc()

    ShapeCalc is the next step in Shape Pack 2 and continues the shift from fixed to parameter‑driven geometry. It builds on ShapeScope by converting the pixel‑based translate-z custom property into a unit‑less numeric value, then re‑applying the pixel units in the transform using calc() to multiply the unit‑less number by 1px with calc(var(–translate-z) * 1px).

    Screenshot of the ShapeCalc component, showing a centered CSS cube on a transparent background

    The following code snippet contains the base HTML for the CSS 3d shape.

    <div class="scene">
            <div class="shape">
                    <div class="shape__face shape__face--front">FRONT<br />ShapeCalc</div>
                    <div class="shape__face shape__face--left">LEFT</div>
                    <div class="shape__face shape__face--back">BACK</div>
                    <div class="shape__face shape__face--right">RIGHT</div>
                    <div class="shape__face shape__face--top">TOP</div>
                    <div class="shape__face shape__face--bottom">BOTTOM</div>
            </div>
    </div>

    The code below shows the current CSS for the shape.

    body
    {
            margin: 0;
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #05060a;
            color: #f5f5f5;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
    }
    
    .scene
    {
            width: 200px;
            height: 200px;
            perspective: 800px;
            perspective-origin: 50% 50%;
    }
    
    @scope (.shape) 
    {
            :scope
            {                             
                    --total-degrees:360;
                    --number-of-sides:4;
                    --degrees-per-side:90;
                    
                    --translate-x:0px;                        
                    --translate-y:0px;
                    --translate-z:100;  
                    
                    --shape-size: 200px;
                    --shape-depth: 200px;
                    --panel-gap: 0px;
    
                    --rotate-x: -20deg;
                    --rotate-y: 25deg;
                    --rotate-z: 0deg;
    
                    --perspective: 800px;
                    --perspective-origin-x: 50%;
                    --perspective-origin-y: 50%;
    
                    --face-front-rotate: 0deg;
                    --face-left-rotate: -90deg;
                    --face-back-rotate: 180deg;
                    --face-right-rotate: 90deg;
                    --face-top-rotate: 90deg;
                    --face-bottom-rotate: -90deg;
                    
                    position: relative;
                    width: 100%;
                    height: 100%;
                    transform-style: preserve-3d;
                    transform: rotateX(-20deg) rotateY(25deg) rotateZ(0deg);
            }
    
            :scope .shape__face
            {
                    position: absolute;
                    width: 100%;
                    height: 100%;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    background: linear-gradient(135deg, #1b1f2a, #2c3244);
                    outline: 1px solid rgba(255, 255, 255, 0.15);
                    box-sizing: border-box;
            }
    
            :scope .shape__face--front      {  transform: rotateY(var(--face-front-rotate))  translateZ(calc(var(--translate-z) * 1px)); }
            :scope .shape__face--left       {  transform: rotateY(var(--face-left-rotate))   translateZ(calc(var(--translate-z) * 1px)); }
            :scope .shape__face--back       {  transform: rotateY(var(--face-back-rotate))   translateZ(calc(var(--translate-z) * 1px)); }
            :scope .shape__face--right      {  transform: rotateY(var(--face-right-rotate))  translateZ(calc(var(--translate-z) * 1px)); }
            :scope .shape__face--top        {  transform: rotateX(var(--face-top-rotate))    translateZ(calc(var(--translate-z) * 1px)); }
            :scope .shape__face--bottom     {  transform: rotateX(var(--face-bottom-rotate)) translateZ(calc(var(--translate-z) * 1px)); }
    }      

    The goal of this step is to separate data from presentation, allowing the shape’s internal math to stay clean, scalable, and re-usable.

    The folder includes:

    • a CSS file showing the unit‑less variable pattern
    • a custom property rewritten as a pure number instead of using a unit after the number
    • updated transforms that applies units through calc() at the moment of use

    What changed from the previous step

    Change: Converted the --translate-z custom property from a pixel value into a unit‑less number, then reapplied the pixel unit only at the moment of use through calc(var(--translate-z) * 1px).

    Reason: To separate raw numeric values from presentation units, making the geometry easier to scale, compute with, and reuse in later components.

    This step shows how geometry can become more flexible without changing the visual output. The cube still behaves the same, but some of its internal values are now mathematical rather than fixed. By removing units from the variable and restoring them only when needed, the shape becomes easier to scale, easier to compute with, and better prepared for future components that rely on dynamic geometry. This continues the progression from ShapeScope, places ShapeCalc within Shape Pack 2, and leads toward the upcoming ShapeFunction component.

  • 6 ShapeScope: modularize with @scope

    6 ShapeScope: modularize with @scope

    ShapeScope is the first step in Shape Pack 2 and the point where the shape becomes fully self‑contained. It builds on ShapeEngine by moving all custom properties and internal selectors into a top‑level @scope (.shape) block, turning the shape into a modular unit that no longer depends on global variables.

    Current state of the shape, shown here as the ShapeScope component on a black background.

    The following code snippet contains the base HTML for the CSS 3d shape.

    <div class="scene">
            <div class="shape">
                    <div class="shape__face shape__face--front">FRONT<br />ShapeScope</div>
                    <div class="shape__face shape__face--left">LEFT</div>
                    <div class="shape__face shape__face--back">BACK</div>
                    <div class="shape__face shape__face--right">RIGHT</div>
                    <div class="shape__face shape__face--top">TOP</div>
                    <div class="shape__face shape__face--bottom">BOTTOM</div>
            </div>
    </div>

    The following code snippet contains the CSS for ShapeScope.

    body
    {
            margin: 0;
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #05060a;
            color: #f5f5f5;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
    }
    
    .scene
    {
            width: 200px;
            height: 200px;
            perspective: 800px;
            perspective-origin: 50% 50%;
    }
    
    @scope (.shape) 
    {
            :scope
            {
                    --shape-size: 200px;
                    --shape-depth: 200px;
                    --panel-count: 6;
                    --panel-gap: 0px;
    
                    --rotate-x: -20deg;
                    --rotate-y: 25deg;
                    --rotate-z: 0deg;
    
                    --perspective: 800px;
                    --perspective-origin-x: 50%;
                    --perspective-origin-y: 50%;
    
                    --animation-duration: 2000ms;
                    --animation-easing: ease-in-out;
    
                    --face-front-rotate: 0deg;
                    --face-left-rotate: -90deg;
                    --face-back-rotate: 180deg;
                    --face-right-rotate: 90deg;
                    --face-top-rotate: 90deg;
                    --face-bottom-rotate: -90deg;
    
                    
                    position: relative;
                    width: 100%;
                    height: 100%;
                    transform-style: preserve-3d;
                    transform: rotateX(-20deg) rotateY(25deg) rotateZ(0deg);
                    transition: transform 2000ms ease-in-out;
            }
    
            :scope .shape__face
            {
                    position: absolute;
                    width: 100%;
                    height: 100%;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                    background: linear-gradient(135deg, #1b1f2a, #2c3244);
                    outline: 1px solid rgba(255, 255, 255, 0.15);
                    box-sizing: border-box;
            }
    
            :scope .shape__face--front      { transform: rotateY(var(--face-front-rotate))  translateZ(calc(var(--shape-depth) / 2)); }
            :scope .shape__face--left       { transform: rotateY(var(--face-left-rotate))   translateZ(calc(var(--shape-depth) / 2)); }
            :scope .shape__face--back       { transform: rotateY(var(--face-back-rotate))   translateZ(calc(var(--shape-depth) / 2)); }
            :scope .shape__face--right      { transform: rotateY(var(--face-right-rotate))  translateZ(calc(var(--shape-depth) / 2)); }
            :scope .shape__face--top        { transform: rotateX(var(--face-top-rotate))    translateZ(calc(var(--shape-depth) / 2)); }
            :scope .shape__face--bottom     { transform: rotateX(var(--face-bottom-rotate)) translateZ(calc(var(--shape-depth) / 2)); }
    }      

    The goal of this step is to shift the shape from a global pattern to an isolated, reusable component.

    The folder includes:

    • a CSS file with the entire shape wrapped inside a single @scope (.shape) block
    • all former :root custom properties moved into :scope, so they apply only to the component
    • updated selectors that ensure every face and transform is local to the .shape instance
    • a short note describing what changed from ShapeEngine

    What changed from the previous step

    Change: Moved all custom properties and internal selectors into a scoped @scope (.shape) block.

    Reason: To isolate the component from global variables and ensure every part of the shape is self‑contained and safe to reuse.

    This step demonstrates how scoping can strengthen a component without altering its appearance or increasing its complexity. The cube renders identically, but its internal logic is now isolated and predictable. By completing the transition started in ShapeEngine and positioning ShapeScope as the opening step of Shape Pack 2, this step prepares the component for the additional capabilities introduced in ShapeCalc.

  • 5 Introducing Shape Pack 2: The next steps

    5 Introducing Shape Pack 2: The next steps

    Shape Pack 2 continues the progression started in the first pack, but shifts the focus from building a shape to strengthening how that shape behaves as a component. While Shape Pack 1 explored structure, variables, and internal organization, Shape Pack 2 introduces two modern CSS features—@scope and function()—and turns the shape into a more modular, adaptable system.

    Screenshot of the ShapeEngine component, showing a centered CSS cube on a black background.
    Current state of the shape, shown here as the ShapeEngine component on a black background.

    Each step in the pack isolates a single improvement, making it easy to see how the component changes as new CSS features come into play. The examples stay small and readable, but now they highlight how scoping, functional values, and other modern tools can shape behavior without adding weight or complexity.

    The format remains straightforward. Every component includes its own HTML and CSS, along with a short explanation of the feature introduced in that step. You can move through the pack in sequence or jump to the parts you’re curious about. And like the first pack, Shape Pack 2 stays framework‑agnostic, so you can drop the pieces into any project or use them as a reference on their own.

    Shape Pack 2 builds on the foundation established in Shape Pack 1 and shows how a shape can evolve through small, focused enhancements. Each step adds a new capability—scoping, unit‑less values, or functional transforms—while keeping the component clear and approachable. You can use the components directly, adapt them, or treat the pack as a foundation for your own variations. The progression begins with the first component, ShapeScope.

  • 4 Shape Engine: Our configurable base shape

    4 Shape Engine: Our configurable base shape

    ShapeEngine is the third step in the pack and the point where the shape gains internal structure. It builds on ShapeWithVariables by using the CSS custom properties defined there to replace the hard‑coded values in the 3d transform.

    Screenshot of the ShapeEngine component, showing a centered CSS cube on a black background.
    Current state of the shape, shown here as the ShapeEngine component on a black background.

    The code below shows the base HTML for the shape.

    <div class="scene">
            <div class="shape">
                    <div class="shape__face shape__face--front">FRONT<br />ShapeEngine</div>
                    <div class="shape__face shape__face--left">LEFT</div>
                    <div class="shape__face shape__face--back">BACK</div>
                    <div class="shape__face shape__face--right">RIGHT</div>
                    <div class="shape__face shape__face--top">TOP</div>
                    <div class="shape__face shape__face--bottom">BOTTOM</div>
            </div>
    </div>

    The code below shows the current CSS for the shape.

    :root
    {
            --shape-size: 200px;
            --shape-depth: 200px;
            --panel-count: 6;
            --panel-gap: 0px;
    
            --rotate-x: -20deg;
            --rotate-y: 25deg;
            --rotate-z: 0deg;
    
            --perspective: 800px;
            --perspective-origin-x: 50%;
            --perspective-origin-y: 50%;
    
            --face-front-rotate: 0deg;
            --face-left-rotate: -90deg;
            --face-back-rotate: 180deg;
            --face-right-rotate: 90deg;
            --face-top-rotate: 90deg;
            --face-bottom-rotate: -90deg;
    }
    
    body
    {
            margin: 0;
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #05060a;
            color: #f5f5f5;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
    }
    
    .scene
    {
            width: 200px;
            height: 200px;
            perspective: 800px;
            perspective-origin: 50% 50%;
    }
    
    .shape
    {
            position: relative;
            width: 100%;
            height: 100%;
            transform-style: preserve-3d;
            transform: rotateX(-20deg) rotateY(25deg) rotateZ(0deg);
    }
    
    .shape__face
    {
            position: absolute;
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            background: linear-gradient(135deg, #1b1f2a, #2c3244);
            outline: 1px solid rgba(255, 255, 255, 0.15);
            box-sizing: border-box;
    }
    
    .shape__face--front { transform: rotateY(var(--face-front-rotate)) translateZ(calc(var(--shape-depth) / 2)); }
    .shape__face--left { transform: rotateY(var(--face-left-rotate)) translateZ(calc(var(--shape-depth) / 2)); }
    .shape__face--back { transform: rotateY(var(--face-back-rotate)) translateZ(calc(var(--shape-depth) / 2)); }
    .shape__face--right { transform: rotateY(var(--face-right-rotate)) translateZ(calc(var(--shape-depth) / 2)); }
    .shape__face--top { transform: rotateX(var(--face-top-rotate)) translateZ(calc(var(--shape-depth) / 2)); }
    .shape__face--bottom { transform: rotateX(var(--face-bottom-rotate)) translateZ(calc(var(--shape-depth) / 2)); }
    

    The goal of this step is to complete the foundation of an extensible CSS shape.

    The folder includes:

    • an updated HTML file with the simple shape
    • a CSS file that replaces the hard‑coded values in the transforms with CSS custom properties
    • a short note describing what changed from ShapeWithVariables

    What changed from the previous step.

    • Change: Completed the move from fixed transform values to fully variable‑driven transforms.
    • Reason: Make the shape fully adjustable through custom properties while keeping the component simple and readable.

    This step completes the core of the shape engine. Building on ShapeWithVariables, it implements the custom properties introduced there, bringing key transform values under custom‑property control and giving the component flexibility without adding complexity. With this foundation in place, Shape Pack 1 reaches its intended baseline, and the next progression begins in Shape Pack 2.

  • 3 ShapeWithVariables: Adding Custom Variables for Future Flexibility

    3 ShapeWithVariables: Adding Custom Variables for Future Flexibility

    ShapeWithVariables is the second step in the pack and the first moment where the shape gains a bit of structure. It takes the minimal form from SimpleShape and introduces a small set of CSS variables to make the component easier to adjust without changing the core rules.

    Screenshot of the ShapeWithVariables component, showing a centered CSS cube on a black background.
    Current state of the shape, shown here as the ShapeWithVariables component on a black background.

    This example shows the base HTML for the CSS 3d shape.

    <div class="scene">
            <div class="shape">
                    <div class="shape__face shape__face--front">FRONT<br />ShapeWithVariables</div>
                    <div class="shape__face shape__face--left">LEFT</div>
                    <div class="shape__face shape__face--back">BACK</div>
                    <div class="shape__face shape__face--right">RIGHT</div>
                    <div class="shape__face shape__face--top">TOP</div>
                    <div class="shape__face shape__face--bottom">BOTTOM</div>
            </div>
    </div>

    The example shows the new CSS for the CSS 3d shape.

    :root
    {
            --shape-size: 200px;
            --shape-depth: 200px;
            --panel-count: 6;
            --panel-gap: 0px;
    
            --rotate-x: -20deg;
            --rotate-y: 25deg;
            --rotate-z: 0deg;
    
            --perspective: 800px;
            --perspective-origin-x: 50%;
            --perspective-origin-y: 50%;
            
            --face-front-rotate: 0deg;
            --face-left-rotate: -90deg;
            --face-back-rotate: 180deg;
            --face-right-rotate: 90deg;
            --face-top-rotate: 90deg;
            --face-bottom-rotate: -90deg;
    }
    
    body
    {
            margin: 0;
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #05060a;
            color: #f5f5f5;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
    }
    
    .scene
    {
            width: 200px;
            height: 200px;
            perspective: 800px;
            perspective-origin: 50% 50%;
    }
    
    .shape
    {
            position: relative;
            width: 100%;
            height: 100%;
            transform-style: preserve-3d;
            transform: rotateX(-20deg) rotateY(25deg) rotateZ(0deg);
    }
    
    .shape__face
    {
            position: absolute;
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            background: linear-gradient(135deg, #1b1f2a, #2c3244);
            outline: 1px solid rgba(255, 255, 255, 0.15);
            box-sizing: border-box;
    }
    
    .shape__face--front { transform: translateZ(100px); }
    .shape__face--left { transform: rotateY(-90deg) translateZ(100px); }
    .shape__face--back { transform: rotateY(180deg) translateZ(100px); }
    .shape__face--right { transform: rotateY(90deg) translateZ(100px); }
    .shape__face--top { transform: rotateX(90deg) translateZ(100px); }
    .shape__face--bottom { transform: rotateX(-90deg) translateZ(100px); }
    

    The goal of this step is flexibility. By moving key values—like size, color, or border thickness—into variables, the shape becomes easier to tune while still staying simple and readable. The component remains minimal, but now it has room to adapt.

    The folder includes:

    • an updated HTML file using the same basic structure
    • a CSS file that adds CSS custom properties
    • a short note describing what changed from ShapeSimple

    What changed from the previous step.

    • Change: Introduced a small set of CSS custom properties to eventually replace fixed values.
    • Reason: Make the shape easier to adjust while keeping the component simple and readable.

    This step builds on ShapeSimple and shows how a shape can evolve without becoming complicated. Within Shape Pack 1, it introduces CSS custom properties as a way to add control while keeping the component small and direct. The next component, ShapeEngine, continues the progression with another small, deliberate layer of structure.

  • 2 ShapeSimple: The First Iteration of Our Shape Component System

    2 ShapeSimple: The First Iteration of Our Shape Component System

    Screenshot of the first component ShapeSimple, showing a centered CSS cube on a black background
    Screenshot of the first component ShapeSimple, showing a centered CSS cube on a black background

    This first step in Shape Pack 1 focuses on clarity. ShapeSimple has no variables, layers, or abstractions so you can see the core structure without distraction. It’s the cleanest version of the shape and the starting point for the entire progression.

    The following code snippet contains the base HTML for the CSS 3d shape.

    <div class="scene">
            <div class="shape">
                    <div class="shape__face shape__face--front">FRONT<br />ShapeSimple</div>
                    <div class="shape__face shape__face--left">LEFT</div>
                    <div class="shape__face shape__face--back">BACK</div>
                    <div class="shape__face shape__face--right">RIGHT</div>
                    <div class="shape__face shape__face--top">TOP</div>
                    <div class="shape__face shape__face--bottom">BOTTOM</div>
            </div>
    </div>
    

    The following code snippet contains the CSS for the CSS 3d shape’s first iteration.

    
    
    
    body
    {
            margin: 0;
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            background: #05060a;
            color: #f5f5f5;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
    }
    
    .scene
    {
            width: 200px;
            height: 200px;
            perspective: 800px;
            perspective-origin: 50% 50%;
    }
    
    .shape
    {
            position: relative;
            width: 100%;
            height: 100%;
            transform-style: preserve-3d;
            transform: rotateX(-20deg) rotateY(25deg) rotateZ(0deg);
    }
    
    .shape__face
    {
            position: absolute;
            width: 100%;
            height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            background: linear-gradient(135deg, #1b1f2a, #2c3244);
            outline: 1px solid rgba(255, 255, 255, 0.15);
            box-sizing: border-box;
    }
    
    .shape__face--front { transform: translateZ(100px); }
    .shape__face--left { transform: rotateY(-90deg) translateZ(100px); }
    .shape__face--back { transform: rotateY(180deg) translateZ(100px); }
    .shape__face--right { transform: rotateY(90deg) translateZ(100px); }
    .shape__face--top { transform: rotateX(90deg) translateZ(100px); }
    .shape__face--bottom { transform: rotateX(-90deg) translateZ(100px); }

    The folder contains three small files:

    • an HTML file with the basic element structure
    • a CSS file with the minimal rules needed to form the shape
    • a short note describing what this step introduces

    What changed from the previous step.

    • Change: This is the first step, so nothing is being modified yet.
    • Reason: Establish a clean, minimal foundation for all later shapes.

    This component sets the foundation for the rest of Shape Pack 1. The next step, ShapeWithVariables, builds on this one, and the later components continue the progression by adding structure or behavior in small, deliberate increments.

  • 1 Introducing Shape Pack 1: A Set of Shape‑Based UI Components

    1 Introducing Shape Pack 1: A Set of Shape‑Based UI Components

    The Shape Pack is a small collection of components that show how a simple shape evolves through clear, incremental steps.

    Screenshot of the first component ShapeSimple, showing a centered CSS cube on a black background
    Current state of the shape, shown here as the SimpleShape component on a black background.

    This pack Shape Pack 1 collects a small set of shape components arranged in a simple progression. Each folder represents one stage of the shape’s build, from the first basic form to more structured versions. The pack started as a way to explore simple, readable component structure.

    The files are kept separate and readable so you can open any component and understand what it’s doing without tracing through a larger system.

    The structure is consistent across the pack. Every component has its own HTML, its own CSS, and a short note describing what changed from the previous step. This makes the progression easy to follow whether you move through it in order or jump between pieces. The pack isn’t tied to a framework or a specific workflow, so it can fit into different projects or be used as a reference on its own.

    The pack shows how a shape can evolve through small, deliberate steps, and it keeps each step isolated so the ideas stay easy to see. You can use the components directly, adapt them, or treat the pack as a starting point for your own variations.