Glassmorphic Architecture Diagram
Build a frosted glass microservices diagram with pure HTML and CSS.
Introduction
30 Second Summary
Architecture diagrams are everywhere in tech. They explain how systems connect, but most look like they were drawn in a basic whiteboard tool.
In this project, you will build a polished architecture diagram using nothing but HTML and CSS. You will create frosted glass cards on a dark canvas, connect them with animated SVG lines, and style everything with a sleek, Linear-inspired aesthetic.
What You'll Build
A single-page architecture diagram showing a microservices system, where six frosted glass cards sit on a CSS Grid over a dark background with soft gradient orbs, connected by flowing SVG dash animations.
By the end of this project, you'll have:
- A dark canvas with gradient orbs that give the glass cards something to refract.
- Six glassmorphic node cards using backdrop-filter and subtle borders, positioned on a CSS Grid to map out a microservices architecture.
- Animated SVG connector lines with flowing dashes and arrowheads that show data flow between services.
- Pulsing status indicators and CSS-only tooltips that bring the diagram to life without a single line of JavaScript (Secret Mission).
Are there any prerequisites?
You just need basic familiarity with HTML and CSS. No frameworks, build tools, or servers are needed. You will write two files and open them directly in your browser.
Set Up Your Project
This entire project is just two files. One HTML file for structure and one CSS file for styling. No frameworks, no build tools, no JavaScript.
In this step, you'll create the project folder, open it in Cursor, and add the HTML boilerplate that loads a monospace font and links your stylesheet.
In this step, get ready to:
- Create the glass-diagram project folder with index.html and style.css.
- Open the folder in Cursor.
- Add HTML boilerplate with Google Fonts and open the page in your browser.
Create the project folder and files
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Terminal (macOS) or PowerShell (Windows) and press Enter to open it.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir glass-diagram
cd glass-diagram
- Create the two project files by running this command:
macOS
touch index.html style.css
Windows
New-Item index.html -ItemType File
New-Item style.css -ItemType File
- Confirm the files exist by running:
ls
You should see index.html and style.css in the output.
Don't see both files?
Make sure you ran cd glass-diagram before creating the files. If you're in the wrong directory, the files will end up somewhere else on your Desktop.
help me find or create my project files
Open the folder in Cursor
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type Cursor and press Enter to open it.
- Click File in the top menu bar.
- Click Open Folder.
- Navigate to your Desktop, select the glass-diagram folder, and click Open.
You should see index.html and style.css in the Explorer sidebar on the left.
Add the HTML boilerplate
Every web page starts with a boilerplate that tells the browser how to render it. Yours loads a monospace font called JetBrains Mono from Google Fonts and links the stylesheet where all your visual styling will go.
- Click index.html in the Explorer sidebar to open it.
- Paste the following code into the file:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>System Architecture</title>
<!-- Load JetBrains Mono from Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<!-- Link to our stylesheet -->
<link rel="stylesheet" href="style.css">
</head>
<body>
</body>
</html>
What does this code do?
- The two preconnect links tell the browser to start connecting to the Google Fonts servers early, which speeds up font loading.
- The third link loads JetBrains Mono in three weights (400, 500, 600). This monospace font gives the page a clean, technical aesthetic that fits an architecture diagram.
- The last link connects style.css to the page. That file is empty for now. You'll fill it in starting in the next step.
- Save index.html by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- Open the file in your browser. In the Explorer sidebar, right-click index.html and select Reveal in Finder (macOS) or Reveal in File Explorer (Windows).
- Double-click index.html in the file browser to open it in your default browser.
You should see a blank white page. Check the browser tab to confirm it says System Architecture.
Why is the page white?
Your style.css file is still empty. The dark background, glass cards, and all the visual styling come in the next steps. A blank white page with the correct tab title means everything is wired up correctly.
Don't see the page title?
Make sure you saved the file after pasting the code. Unsaved files in Cursor show a dot on the tab.
Check that the file is named index.html and not index.html.txt. Some systems hide file extensions by default, which can cause this.
help me debug my blank HTML page
Awesome, I've got everything!
Double check you've saved index.html and you're good to go.
I'd like to double check the full code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>System Architecture</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
</body>
</html>
Pro tip: keep your browser open
Keep this browser tab open for the rest of the project. Every time you save a file in Cursor, just switch to the browser and refresh the page to see your changes.
Your project is set up and ready. Next, you'll build the dark canvas with glowing gradient orbs that give your glass cards something to refract.
Build the Dark Canvas with Gradient Orbs
Your project files are set up and your blank page is open in the browser. Now it's time to build the visual foundation that the entire diagram sits on.
You'll create a dark, Linear-inspired page layout with a styled header. Then you'll add soft gradient orbs to the background that give the frosted glass cards (coming in the next step) something to actually refract.
In this step, get ready to:
- Add the page wrapper, header, and diagram container to your HTML.
- Style the dark background, centered layout, and header elements in CSS.
- Create gradient orbs that produce the color needed for the glassmorphism effect.
Add the page structure to your HTML
Every page needs a skeleton before it gets styled. You'll add a wrapper, a header with three elements (a badge, title, and subtitle), and an empty diagram container that you'll fill later.
- In Cursor, open index.html.
- Find the <body> tags, then add the following code between them:
<div class="page">
<header class="header">
<!-- Badge pill at the top of the header -->
<span class="badge">Architecture</span>
<!-- Main heading -->
<h1 class="title">System Overview</h1>
<!-- Subheading with context -->
<p class="subtitle">Microservices / Production</p>
</header>
<!-- Container for the diagram area -->
<div class="diagram">
</div>
</div>
What does this code do?
- .page wraps the entire layout. You'll use it to set a max width and center everything.
- The header has three elements: a small .badge pill, a .title heading, and a .subtitle line with context.
- .diagram is empty for now. This is where the gradient orbs, connector lines, and node cards will go in later steps.
- Save index.html.
- Refresh your browser. You should see the raw text "Architecture", "System Overview", and "Microservices / Production" stacked on the page with no styling.
Don't see any text?
- Make sure you pasted the code between the opening <body> and closing </body> tags, not inside the <head> section.
- Check that the file is saved. Unsaved changes won't show in the browser.
- help me debug why my HTML page is blank
Style the dark background and header
Time to transform that plain white page into a dark, polished canvas. You'll set the background color, center the layout with flexbox, and apply JetBrains Mono as the font.
- Open style.css in Cursor. Add the base reset, body styles, and page layout by pasting the following code:
/* Reset default browser margins and padding */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* Dark background, centered layout, monospace font */
body {
background: #0a0a0c;
color: rgba(255, 255, 255, 0.9);
font-family: 'JetBrains Mono', monospace;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
/* ---- Page Layout ---- */
/* Constrain content width and add breathing room */
.page {
width: 100%;
max-width: 900px;
padding: 64px 32px;
}
What does this code do?
- The * selector resets every element's margin and padding to zero. This prevents inconsistent spacing across browsers.
- body gets the dark background (#0a0a0c), which is almost pure black. display: flex with align-items: center and justify-content: center centers the entire page vertically and horizontally.
- .page caps the content width at 900px so the diagram doesn't stretch across ultra-wide screens.
- Below the .page rule, add the header styles by pasting this code:
/* ---- Header ---- */
/* Center all header text, add space below */
.header {
text-align: center;
margin-bottom: 64px;
}
/* Small uppercase pill label */
.badge {
display: inline-block;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
color: #5e6ad2;
background: rgba(94, 106, 210, 0.1);
border: 1px solid rgba(94, 106, 210, 0.2);
padding: 4px 12px;
border-radius: 100px;
margin-bottom: 16px;
}
/* Main heading with tight letter spacing */
.title {
font-size: 28px;
font-weight: 600;
letter-spacing: -0.02em;
margin-bottom: 8px;
}
/* Muted subheading */
.subtitle {
font-size: 13px;
color: rgba(255, 255, 255, 0.4);
}
What does this code do?
- .badge creates a rounded pill label. The color #5e6ad2 is Linear's signature desaturated blue. The background and border use that same blue at very low opacity for a subtle tinted chip effect.
- .title uses negative letter spacing (-0.02em) to tighten the characters. This is a common technique in modern design to make headings feel more refined.
- .subtitle uses a low-opacity white (rgba(255, 255, 255, 0.4)) to visually recede behind the title. This creates a clear text hierarchy without using a different color.
- Save style.css and refresh your browser.
- You should see a dark page with a small blue "Architecture" pill, a white "System Overview" heading, and a muted "Microservices / Production" subtitle. All text is centered.
Page still looks unstyled?
- Check that the <link rel="stylesheet" href="style.css"> line exists in the <head> of your HTML. Without it, the browser can't find your CSS.
- Make sure style.css is in the same folder as index.html. If the file is nested in a subfolder, the link won't resolve.
- If the font looks different from a monospace style, confirm that the Google Fonts <link> tags from Step 1 are in your <head> section.
- help me debug why my CSS styles aren't showing
Add the gradient orbs
Glassmorphism creates a frosted-glass look by blurring the content behind an element. On a flat, solid dark background, there's nothing to blur, so the effect is invisible.
Gradient orbs solve this. They're large, heavily blurred circles with subtle color that sit behind the glass cards. When the glass blurs them, you get the soft, diffused light that makes the frosted effect work.
- In index.html, find the empty <div class="diagram"> container. Add three orb divs inside it:
<!-- Gradient orbs that give glass something to refract -->
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
What does this code do?
Each div gets a shared orb class (for the base circle and blur styles) plus a unique class (orb-1, orb-2, orb-3) for individual size, color, and position.
- Save index.html.
Awesome, I've got everything!
Great. Double check you've saved index.html before moving on to the CSS.
I'd like to double check the full code
Here's the full index.html so far. Make sure yours matches:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>System Architecture</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="page">
<header class="header">
<span class="badge">Architecture</span>
<h1 class="title">System Overview</h1>
<p class="subtitle">Microservices / Production</p>
</header>
<div class="diagram">
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
</div>
</div>
</body>
</html>
The orbs won't be visible yet because they have no styles. Time to fix that.
- Switch to style.css. Below the .subtitle rule, add the diagram container and orb styles by pasting this code:
/* ---- Diagram Container ---- */
/* Fixed-size container so SVG coordinates align with the grid */
.diagram {
position: relative;
width: 800px;
height: 560px;
margin: 0 auto;
}
/* ---- Background Orbs ---- */
/* Base orb: a circle blurred beyond recognition */
.orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
pointer-events: none;
}
/* Purple orb, top-left area */
.orb-1 {
width: 300px;
height: 300px;
background: rgba(94, 106, 210, 0.08);
top: 10%;
left: 15%;
}
/* Violet orb, right side */
.orb-2 {
width: 250px;
height: 250px;
background: rgba(139, 92, 246, 0.06);
top: 50%;
right: 10%;
}
/* Blue orb, bottom-center area */
.orb-3 {
width: 200px;
height: 200px;
background: rgba(59, 130, 246, 0.05);
bottom: 5%;
left: 35%;
}
What does this code do?
- .diagram is a fixed 800x560 pixel container. It uses position: relative so that child elements (orbs, connectors, nodes) can be positioned absolutely inside it.
- .orb turns each div into a circle with border-radius: 50%. The filter: blur(80px) smears the color out so it becomes a soft ambient glow instead of a hard shape.
- Each orb uses a different rgba color at very low opacity (0.05 to 0.08). This keeps the colors subtle and desaturated, matching Linear's calm aesthetic.
- pointer-events: none makes the orbs click-through so they never block interaction with the cards that sit on top of them.
Why is the container a fixed size?
In later steps, you'll add SVG connector lines that use absolute pixel coordinates to draw paths between nodes. A fixed container ensures the SVG's coordinate system maps 1:1 with the CSS Grid positions.
- Save style.css and refresh your browser.
- You should see the styled header above a dark area that has very soft, blurred patches of purple, violet, and blue. The orbs are subtle. Look closely at the background below the header to spot them.
Don't see the gradient orbs?
- The orbs are intentionally very faint. Try looking at the page in a dimly lit room or increasing your monitor brightness.
- Make sure the three <div class="orb ..."> elements are inside the <div class="diagram"> container in your HTML, not outside it.
- Check that .diagram has position: relative and the orbs have position: absolute. Without these, the orbs stack normally instead of overlapping.
- help me figure out why my gradient orbs aren't showing
Awesome, I've got everything!
Great. Double check you've saved both files before moving on.
I'd like to double check the full code
Here's the full style.css so far. Make sure yours matches:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #0a0a0c;
color: rgba(255, 255, 255, 0.9);
font-family: 'JetBrains Mono', monospace;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
/* ---- Page Layout ---- */
.page {
width: 100%;
max-width: 900px;
padding: 64px 32px;
}
/* ---- Header ---- */
.header {
text-align: center;
margin-bottom: 64px;
}
.badge {
display: inline-block;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
color: #5e6ad2;
background: rgba(94, 106, 210, 0.1);
border: 1px solid rgba(94, 106, 210, 0.2);
padding: 4px 12px;
border-radius: 100px;
margin-bottom: 16px;
}
.title {
font-size: 28px;
font-weight: 600;
letter-spacing: -0.02em;
margin-bottom: 8px;
}
.subtitle {
font-size: 13px;
color: rgba(255, 255, 255, 0.4);
}
/* ---- Diagram Container ---- */
.diagram {
position: relative;
width: 800px;
height: 560px;
margin: 0 auto;
}
/* ---- Background Orbs ---- */
.orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
pointer-events: none;
}
.orb-1 {
width: 300px;
height: 300px;
background: rgba(94, 106, 210, 0.08);
top: 10%;
left: 15%;
}
.orb-2 {
width: 250px;
height: 250px;
background: rgba(139, 92, 246, 0.06);
top: 50%;
right: 10%;
}
.orb-3 {
width: 200px;
height: 200px;
background: rgba(59, 130, 246, 0.05);
bottom: 5%;
left: 35%;
}
Your dark canvas is ready with a polished header and ambient gradient light. Next up, you'll place six frosted glass node cards on a CSS Grid to build the architecture diagram itself.
Create the Glassmorphic Node Cards
Your dark canvas is set up with those soft gradient orbs floating in the background. Those orbs exist for a reason. Glassmorphism is nearly invisible on a solid dark background because there is nothing behind the glass to refract.
Now you will build the six service nodes that make up the architecture diagram. Each node is a frosted glass card positioned on a CSS Grid, floating over those orbs so the glass effect has something to blur through.
In this step, get ready to:
- Add six node card divs to the HTML with icons, labels, and descriptions.
- Position the nodes on a 3-column CSS Grid layout.
- Apply the glassmorphism effect to each card using backdrop-filter and translucent backgrounds.
Add the node card markup
The architecture diagram represents a microservices system with six components: a client, an API gateway, an auth service, an app service, a cache, and a database.
Each node gets three pieces of information: a decorative icon character, a label for the service name, and a meta description for the technology it uses.
- In index.html, find the last orb div (<div class="orb orb-3"></div>) inside the .diagram container.
- Add this code directly below it, still inside the .diagram div:
<!-- Node cards grid -->
<div class="nodes">
<!-- Top center: client entry point -->
<div class="node node-client">
<span class="node-icon">◇</span>
<span class="node-label">Client</span>
<span class="node-meta">React SPA</span>
</div>
<!-- Center: API gateway -->
<div class="node node-gateway">
<span class="node-icon">⬡</span>
<span class="node-label">API Gateway</span>
<span class="node-meta">Kong</span>
</div>
<!-- Left middle: auth service -->
<div class="node node-auth">
<span class="node-icon">◆</span>
<span class="node-label">Auth Service</span>
<span class="node-meta">JWT + OAuth 2.0</span>
</div>
<!-- Right middle: app service -->
<div class="node node-app">
<span class="node-icon">◆</span>
<span class="node-label">App Service</span>
<span class="node-meta">Node.js</span>
</div>
<!-- Bottom left: cache layer -->
<div class="node node-cache">
<span class="node-icon">◈</span>
<span class="node-label">Cache</span>
<span class="node-meta">Redis</span>
</div>
<!-- Bottom right: database -->
<div class="node node-db">
<span class="node-icon">◈</span>
<span class="node-label">Database</span>
<span class="node-meta">PostgreSQL</span>
</div>
</div>
What does this code do?
- The .nodes wrapper will become the CSS Grid container that positions all six cards.
- Each card has two classes: .node for shared glass styles and a unique class like .node-client for grid placement.
- The three <span> elements inside each card separate the icon, label, and meta text so you can style them independently.
- Save index.html and refresh your browser.
- You should see six lines of plain white text stacked vertically on the dark background. The text includes the icon characters, service names, and technology labels. No styling yet.
Don't see any new text?
- Make sure the .nodes div is inside the .diagram div, not after it.
- Check that every opening <div> has a matching closing </div>. There are 8 divs in total (1 wrapper + 6 nodes + each node is 1 div).
- Still stuck? Help me figure out why my node cards aren't showing up in the browser.
Position the nodes with CSS Grid
Right now the six cards stack vertically because the browser lays out divs top to bottom by default. You need them arranged in a diamond-like pattern: one at the top, one below it, two branching left and right, and two more at the bottom.
- Add the grid layout and placement rules to the bottom of style.css:
/* ---- Node Grid ---- */
.nodes {
display: grid;
grid-template-columns: 1fr 1fr 1fr; /* Three equal columns */
grid-template-rows: repeat(4, 140px); /* Four rows, 140px each */
width: 100%;
height: 100%;
position: relative;
z-index: 2; /* Sit above the orbs */
}
/* ---- Node Placement ---- */
.node-client { grid-column: 2; grid-row: 1; } /* Top center */
.node-gateway { grid-column: 2; grid-row: 2; } /* Center */
.node-auth { grid-column: 1; grid-row: 3; } /* Left middle */
.node-app { grid-column: 3; grid-row: 3; } /* Right middle */
.node-cache { grid-column: 1; grid-row: 4; } /* Bottom left */
.node-db { grid-column: 3; grid-row: 4; } /* Bottom right */
What does this code do?
- The grid has 3 columns and 4 rows. Each row is 140px tall, which totals 560px to match the diagram container height.
- Each node-specific class (.node-client, .node-gateway, etc.) places that card into a specific grid cell using grid-column and grid-row.
- The z-index: 2 ensures the grid of nodes sits above the blurred orbs in the background.
- Save style.css and refresh your browser.
- You should see the six service names distributed across the page in a tree-like pattern. Client and Gateway are centered at the top. Auth and App are on the left and right. Cache and Database are at the bottom.
Nodes still stacking vertically?
- Confirm that .nodes has display: grid. Without it, the grid properties are ignored.
- Check that each placement class (.node-client, etc.) matches the class name in your HTML exactly. CSS class names are case-sensitive.
- Need help? Help me debug why my CSS Grid layout isn't positioning the nodes.
Apply the glassmorphism effect
The nodes are positioned, but they are still just plain text. Time to turn them into frosted glass cards.
- Add the glass card styles to the bottom of style.css:
/* ---- Node Card ---- */
.node {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 160px;
height: 80px;
/* Glassmorphism layers */
background: rgba(255, 255, 255, 0.03); /* Translucent fill */
backdrop-filter: blur(12px); /* Frosted glass blur */
-webkit-backdrop-filter: blur(12px); /* Safari support */
border: 1px solid rgba(255, 255, 255, 0.06); /* Subtle edge reflection */
border-radius: 12px;
place-self: center; /* Center card in its grid cell */
}
What is glassmorphism?
Glassmorphism creates a frosted glass look using four layers working together:
- Translucency: rgba(255, 255, 255, 0.03) gives the card a barely-there white fill.
- Refraction: backdrop-filter: blur(12px) blurs everything behind the card, creating the frosted effect.
- Surface reflection: The thin border at 0.06 opacity mimics light catching the edge of glass.
- place-self: center centers each 160x80px card inside its grid cell so the card floats in the middle of its space.
On dark backgrounds, the opacity values stay very low (0.03 for background, 0.06 for border). Higher values would make the cards look like opaque white boxes instead of glass.
- Save style.css and refresh your browser.
- Each node should now appear as a small rounded rectangle with a subtle border. The glass effect is very faint against the dark background. If an orb sits behind a card, you will see a slight frosted blur through it.
Cards look completely invisible?
- The glass is intentionally subtle. Look closely for thin borders around the text. If you see no borders at all, check that the border property uses rgba(255, 255, 255, 0.06) and not 0.006.
- Make sure you include both backdrop-filter and -webkit-backdrop-filter. Safari requires the -webkit- prefix.
- Need help? Help me figure out why my glassmorphism effect isn't visible on the node cards.
- Now style the text inside each card by adding these rules to the bottom of style.css:
.node-icon {
font-size: 10px;
color: rgba(94, 106, 210, 0.6); /* Accent color, subdued */
margin-bottom: 4px;
}
.node-label {
font-size: 13px;
font-weight: 500;
color: rgba(255, 255, 255, 0.9); /* Near-white for readability */
}
.node-meta {
font-size: 10px;
color: rgba(255, 255, 255, 0.3); /* Dimmed secondary text */
margin-top: 2px;
}
What does this code do?
- The icon uses the accent color #5e6ad2 at 60% opacity for a subtle branded touch without drawing too much attention.
- The label is near-white (0.9 opacity) so it is the most prominent text on the card.
- The meta text is dimmed to 0.3 opacity, creating a clear visual hierarchy: icon, label, then supporting detail.
- Save style.css and refresh your browser.
- Each card should now show a small colored icon at the top, a white service name in the middle, and a dimmed technology label at the bottom. The text is compact and centered inside the glass rectangle.
Text looks too big or misaligned?
- Check that .node has display: flex and flex-direction: column. Without flexbox, the three spans will sit side by side instead of stacking vertically.
- Confirm the font sizes are 10px for icon and meta, 13px for label. Larger values will overflow the 80px tall card.
- Still stuck? Help me debug the text styling inside my glassmorphic node cards.
Awesome, I've got everything!
Great. Double check that you have saved both index.html and style.css before moving on.
I'd like to double check the full code
Here is the full index.html so far:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>System Architecture</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="page">
<header class="header">
<span class="badge">Architecture</span>
<h1 class="title">System Overview</h1>
<p class="subtitle">Microservices / Production</p>
</header>
<div class="diagram">
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
<div class="nodes">
<div class="node node-client">
<span class="node-icon">◇</span>
<span class="node-label">Client</span>
<span class="node-meta">React SPA</span>
</div>
<div class="node node-gateway">
<span class="node-icon">⬡</span>
<span class="node-label">API Gateway</span>
<span class="node-meta">Kong</span>
</div>
<div class="node node-auth">
<span class="node-icon">◆</span>
<span class="node-label">Auth Service</span>
<span class="node-meta">JWT + OAuth 2.0</span>
</div>
<div class="node node-app">
<span class="node-icon">◆</span>
<span class="node-label">App Service</span>
<span class="node-meta">Node.js</span>
</div>
<div class="node node-cache">
<span class="node-icon">◈</span>
<span class="node-label">Cache</span>
<span class="node-meta">Redis</span>
</div>
<div class="node node-db">
<span class="node-icon">◈</span>
<span class="node-label">Database</span>
<span class="node-meta">PostgreSQL</span>
</div>
</div>
</div>
</div>
</body>
</html>
And here is the full style.css so far:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #0a0a0c;
color: rgba(255, 255, 255, 0.9);
font-family: 'JetBrains Mono', monospace;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
/* ---- Page Layout ---- */
.page {
width: 100%;
max-width: 900px;
padding: 64px 32px;
}
/* ---- Header ---- */
.header {
text-align: center;
margin-bottom: 64px;
}
.badge {
display: inline-block;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
color: #5e6ad2;
background: rgba(94, 106, 210, 0.1);
border: 1px solid rgba(94, 106, 210, 0.2);
padding: 4px 12px;
border-radius: 100px;
margin-bottom: 16px;
}
.title {
font-size: 28px;
font-weight: 600;
letter-spacing: -0.02em;
margin-bottom: 8px;
}
.subtitle {
font-size: 13px;
color: rgba(255, 255, 255, 0.4);
}
/* ---- Diagram Container ---- */
.diagram {
position: relative;
width: 800px;
height: 560px;
margin: 0 auto;
}
/* ---- Background Orbs ---- */
.orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
pointer-events: none;
}
.orb-1 {
width: 300px;
height: 300px;
background: rgba(94, 106, 210, 0.08);
top: 10%;
left: 15%;
}
.orb-2 {
width: 250px;
height: 250px;
background: rgba(139, 92, 246, 0.06);
top: 50%;
right: 10%;
}
.orb-3 {
width: 200px;
height: 200px;
background: rgba(59, 130, 246, 0.05);
bottom: 5%;
left: 35%;
}
/* ---- Node Grid ---- */
.nodes {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: repeat(4, 140px);
width: 100%;
height: 100%;
position: relative;
z-index: 2;
}
/* ---- Node Placement ---- */
.node-client { grid-column: 2; grid-row: 1; }
.node-gateway { grid-column: 2; grid-row: 2; }
.node-auth { grid-column: 1; grid-row: 3; }
.node-app { grid-column: 3; grid-row: 3; }
.node-cache { grid-column: 1; grid-row: 4; }
.node-db { grid-column: 3; grid-row: 4; }
/* ---- Node Card ---- */
.node {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 160px;
height: 80px;
background: rgba(255, 255, 255, 0.03);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 12px;
place-self: center;
}
.node-icon {
font-size: 10px;
color: rgba(94, 106, 210, 0.6);
margin-bottom: 4px;
}
.node-label {
font-size: 13px;
font-weight: 500;
color: rgba(255, 255, 255, 0.9);
}
.node-meta {
font-size: 10px;
color: rgba(255, 255, 255, 0.3);
margin-top: 2px;
}
Your six glass cards are positioned and styled. The diagram already looks clean and structured. Next up, you will draw animated SVG connector lines between the nodes to show how data flows through the system.
Draw Animated SVG Connectors
Your glass cards are placed on the grid, but right now they look like six isolated boxes floating in space. An architecture diagram needs to show how data flows between services.
In this step, you'll draw connector lines between the nodes using inline SVG. Each line gets a dashed stroke that flows forward using a CSS keyframe animation, showing the direction of data movement with zero JavaScript.
In this step, get ready to:
- Add an inline SVG with arrowhead markers and five connector paths.
- Style the connectors with dashed strokes and staggered animation delays.
- Create a keyframe animation that makes the dashes flow along each path.
Add the SVG connector markup
The connectors are SVG <path> elements drawn inside an <svg> that sits on top of the diagram container. Each path's coordinates match the grid cell centers so the lines connect the correct nodes.
- In index.html, find the closing </div> of the last orb (orb-3). Add the following SVG block below it, before the opening <div class="nodes"> tag:
<!-- SVG connector lines between nodes -->
<svg class="connectors" viewBox="0 0 800 560" fill="none">
<defs>
<!-- Arrowhead marker for line endpoints -->
<marker id="arrowhead" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" stroke="rgba(255,255,255,0.3)" stroke-width="1.5" fill="none" />
</marker>
</defs>
<!-- Client to Gateway (straight down) -->
<path class="connector" d="M400,110 L400,170" marker-end="url(#arrowhead)" />
<!-- Gateway to Auth (curve left) -->
<path class="connector delay-1" d="M400,250 C400,280 133,280 133,310" marker-end="url(#arrowhead)" />
<!-- Gateway to App (curve right) -->
<path class="connector delay-2" d="M400,250 C400,280 667,280 667,310" marker-end="url(#arrowhead)" />
<!-- App to Cache (curve left) -->
<path class="connector delay-3" d="M667,390 C667,420 133,420 133,450" marker-end="url(#arrowhead)" />
<!-- App to Database (straight down) -->
<path class="connector delay-4" d="M667,390 L667,450" marker-end="url(#arrowhead)" />
</svg>
What does this code do?
- The <defs> block defines a reusable arrowhead marker. orient="auto-start-reverse" makes the arrowhead automatically rotate to follow each path's direction.
- Each <path> draws one connector line. Straight connections use the L (line) command, while diagonal connections use the C (cubic bezier curve) command for smooth arcs.
- The coordinates like 400,110 and 133,310 map to the center-bottom and center-top of each node's grid cell. The SVG viewBox matches the diagram container size (800x560) so coordinates line up pixel-for-pixel with the CSS Grid.
- marker-end="url(#arrowhead)" attaches the arrowhead to the end of each path. SVG markers do not inherit colors from their parent path, so the arrowhead's stroke color is set explicitly in the marker definition.
- Save index.html.
- Refresh your browser. You won't see the connectors yet because they have no stroke styling. You'll add that next.
Page looks broken or blank?
- Make sure the <svg> block is inside the .diagram div, not outside it. It should sit between the orb divs and the .nodes wrapper.
- Check that you haven't accidentally deleted a closing </div> tag for one of the orb elements. A missing tag can throw off the entire layout.
- Help me debug my SVG connector layout.
Style the connector paths
The SVG paths exist in the DOM but they're invisible without stroke styles. You'll give them a subtle dashed appearance and position the SVG layer to overlay the diagram exactly.
- In style.css, find the /* ---- Background Orbs ---- */ section. After all the orb styles (.orb-3 is the last one), add the following connector styles:
/* ---- SVG Connectors ---- */
.connectors {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none; /* clicks pass through to nodes below */
z-index: 1;
}
.connector {
stroke: rgba(255, 255, 255, 0.12); /* subtle white lines */
stroke-width: 1;
stroke-dasharray: 6 4; /* 6px dash, 4px gap */
animation: flow 2s linear infinite;
}
/* Stagger animation start times for each connector */
.delay-1 { animation-delay: 0.3s; }
.delay-2 { animation-delay: 0.6s; }
.delay-3 { animation-delay: 0.9s; }
.delay-4 { animation-delay: 1.2s; }
What does this code do?
- .connectors is positioned absolutely so the SVG sits directly on top of the diagram container. pointer-events: none lets mouse clicks pass right through the SVG layer so you can still interact with the node cards underneath.
- stroke-dasharray: 6 4 creates a repeating pattern of 6px dashes and 4px gaps. This gives the connectors a dotted-line look instead of a solid stroke.
- The delay-1 through delay-4 classes stagger when each connector starts animating. This prevents all five lines from pulsing in sync, creating a more natural cascading effect.
- Save style.css and refresh your browser.
- You should see thin, dashed white lines connecting your node cards. The lines are static right now because the flow animation referenced in the CSS doesn't exist yet.
Don't see any lines?
- Check that the SVG element in your HTML has class="connectors". Without this class, the CSS won't target it.
- Make sure your .diagram container still has position: relative in your CSS. The absolutely positioned SVG needs a positioned parent to anchor to.
- Verify that the .nodes wrapper has z-index: 2 and .connectors has z-index: 1. If the nodes layer is behind the SVG, the lines may be hidden.
- Help me figure out why my SVG connector lines aren't showing up.
Animate the flowing dashes
The dashes on each connector are static right now. A keyframe animation that shifts the dash pattern along the path creates the illusion of data flowing from one node to the next.
- In style.css, add this keyframe animation right after the delay classes:
@keyframes flow {
to {
stroke-dashoffset: -10; /* shift dashes by one full pattern (6 + 4 = 10px) */
}
}
Why does -10 create a seamless loop?
Your stroke-dasharray: 6 4 creates a repeating pattern of 6px dash + 4px gap, totaling 10px. Shifting stroke-dashoffset by exactly -10 moves the pattern by one full cycle. When the animation loops back to the start, the dashes are in the exact same position, so the flow looks seamless.
The negative value makes the dashes flow forward along the path direction. A positive value would make them flow backward.
- Save style.css and refresh your browser.
- The dashed lines should now be flowing along each connector path. Watch closely and you'll notice each line starts its animation at a slightly different time thanks to the staggered delays.
Dashes aren't moving?
- Check that the animation name in .connector matches the @keyframes name exactly. Both must say flow.
- Make sure stroke-dashoffset is set to -10 (negative). A value of 0 or a positive number won't produce visible movement.
- Help me debug my flowing dash animation.
Awesome, I've got everything!
Make sure you've saved both index.html and style.css before moving on.
I'd like to double check the full code
Here is what your index.html should look like at this point:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>System Architecture</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="page">
<header class="header">
<span class="badge">Architecture</span>
<h1 class="title">System Overview</h1>
<p class="subtitle">Microservices / Production</p>
</header>
<div class="diagram">
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
<svg class="connectors" viewBox="0 0 800 560" fill="none">
<defs>
<marker id="arrowhead" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" stroke="rgba(255,255,255,0.3)" stroke-width="1.5" fill="none" />
</marker>
</defs>
<path class="connector" d="M400,110 L400,170" marker-end="url(#arrowhead)" />
<path class="connector delay-1" d="M400,250 C400,280 133,280 133,310" marker-end="url(#arrowhead)" />
<path class="connector delay-2" d="M400,250 C400,280 667,280 667,310" marker-end="url(#arrowhead)" />
<path class="connector delay-3" d="M667,390 C667,420 133,420 133,450" marker-end="url(#arrowhead)" />
<path class="connector delay-4" d="M667,390 L667,450" marker-end="url(#arrowhead)" />
</svg>
<div class="nodes">
<div class="node node-client">
<span class="node-icon">◇</span>
<span class="node-label">Client</span>
<span class="node-meta">React SPA</span>
</div>
<div class="node node-gateway">
<span class="node-icon">⬡</span>
<span class="node-label">API Gateway</span>
<span class="node-meta">Kong</span>
</div>
<div class="node node-auth">
<span class="node-icon">◆</span>
<span class="node-label">Auth Service</span>
<span class="node-meta">JWT + OAuth 2.0</span>
</div>
<div class="node node-app">
<span class="node-icon">◆</span>
<span class="node-label">App Service</span>
<span class="node-meta">Node.js</span>
</div>
<div class="node node-cache">
<span class="node-icon">◈</span>
<span class="node-label">Cache</span>
<span class="node-meta">Redis</span>
</div>
<div class="node node-db">
<span class="node-icon">◈</span>
<span class="node-label">Database</span>
<span class="node-meta">PostgreSQL</span>
</div>
</div>
</div>
</div>
</body>
</html>
And here is what your style.css should look like:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #0a0a0c;
color: rgba(255, 255, 255, 0.9);
font-family: 'JetBrains Mono', monospace;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
/* ---- Page Layout ---- */
.page {
width: 100%;
max-width: 900px;
padding: 64px 32px;
}
/* ---- Header ---- */
.header {
text-align: center;
margin-bottom: 64px;
}
.badge {
display: inline-block;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
color: #5e6ad2;
background: rgba(94, 106, 210, 0.1);
border: 1px solid rgba(94, 106, 210, 0.2);
padding: 4px 12px;
border-radius: 100px;
margin-bottom: 16px;
}
.title {
font-size: 28px;
font-weight: 600;
letter-spacing: -0.02em;
margin-bottom: 8px;
}
.subtitle {
font-size: 13px;
color: rgba(255, 255, 255, 0.4);
}
/* ---- Diagram Container ---- */
.diagram {
position: relative;
width: 800px;
height: 560px;
margin: 0 auto;
}
/* ---- Background Orbs ---- */
.orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
pointer-events: none;
}
.orb-1 {
width: 300px;
height: 300px;
background: rgba(94, 106, 210, 0.08);
top: 10%;
left: 15%;
}
.orb-2 {
width: 250px;
height: 250px;
background: rgba(139, 92, 246, 0.06);
top: 50%;
right: 10%;
}
.orb-3 {
width: 200px;
height: 200px;
background: rgba(59, 130, 246, 0.05);
bottom: 5%;
left: 35%;
}
/* ---- SVG Connectors ---- */
.connectors {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
.connector {
stroke: rgba(255, 255, 255, 0.12);
stroke-width: 1;
stroke-dasharray: 6 4;
animation: flow 2s linear infinite;
}
.delay-1 { animation-delay: 0.3s; }
.delay-2 { animation-delay: 0.6s; }
.delay-3 { animation-delay: 0.9s; }
.delay-4 { animation-delay: 1.2s; }
@keyframes flow {
to {
stroke-dashoffset: -10;
}
}
/* ---- Node Grid ---- */
.nodes {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: repeat(4, 140px);
width: 100%;
height: 100%;
position: relative;
z-index: 2;
}
/* ---- Node Placement ---- */
.node-client { grid-column: 2; grid-row: 1; }
.node-gateway { grid-column: 2; grid-row: 2; }
.node-auth { grid-column: 1; grid-row: 3; }
.node-app { grid-column: 3; grid-row: 3; }
.node-cache { grid-column: 1; grid-row: 4; }
.node-db { grid-column: 3; grid-row: 4; }
/* ---- Node Card ---- */
.node {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 160px;
height: 80px;
background: rgba(255, 255, 255, 0.03);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 12px;
place-self: center;
}
.node-icon {
font-size: 10px;
color: rgba(94, 106, 210, 0.6);
margin-bottom: 4px;
}
.node-label {
font-size: 13px;
font-weight: 500;
color: rgba(255, 255, 255, 0.9);
}
.node-meta {
font-size: 10px;
color: rgba(255, 255, 255, 0.3);
margin-top: 2px;
}
Your diagram now has animated data flow lines connecting every service. Next up, you'll add hover effects that make the glass cards glow and lift when you interact with them.
Add Hover Effects and Final Polish
Your SVG connectors are flowing between nodes, bringing the diagram to life with animated data paths. There is one more thing that separates a demo from a polished piece: interactivity.
In this step, you'll add hover effects that make each card respond to the cursor. A subtle lift, a soft glow, and smooth transitions will make every node feel alive.
In this step, get ready to:
- Add hover effects to each node card with a glow and lift.
- Add smooth CSS transitions to animate the hover state.
- Verify the complete diagram renders correctly in your browser.
Add hover effects to the node cards
Right now, the glass cards are static. Hover effects give visual feedback when someone interacts with the diagram, making each node feel like a real UI element instead of a flat drawing.
- In style.css, add the .node:hover rule directly below the closing } of the .node rule by pasting this code:
.node:hover {
/* Slightly brighter glass on hover */
background: rgba(255, 255, 255, 0.06);
/* Make the border more visible */
border-color: rgba(255, 255, 255, 0.12);
/* Soft glow using the accent color */
box-shadow: 0 0 40px rgba(94, 106, 210, 0.08);
/* Lift the card up slightly */
transform: translateY(-2px);
}
What does this code do?
- background: rgba(255, 255, 255, 0.06) doubles the glass opacity from 0.03, making the card visibly brighter under the cursor.
- border-color: rgba(255, 255, 255, 0.12) doubles the border opacity so the card's edge becomes more defined on hover.
- box-shadow: 0 0 40px rgba(94, 106, 210, 0.08) adds a wide, diffused glow using the Linear accent blue. The low opacity keeps it subtle.
- transform: translateY(-2px) shifts the card 2 pixels upward, creating a lift effect that makes the node feel like it is rising off the surface.
- Save style.css and refresh your browser.
- Hover over any node card. The card should snap to a brighter state with a visible lift.
The change is instant right now. The card jumps to its hovered state without any animation. You'll fix that next.
Don't see the hover effect?
- Make sure the .node:hover block is directly below the closing } of the .node rule, not inside it.
- Check that the selector says .node:hover with a colon, not .node hover with a space. The colon targets the same element's hover state. A space targets a child element.
- Make sure you saved the file. Unsaved changes won't appear in the browser.
My hover effect isn't showing up on the glass cards.
Add smooth transitions to the base state
CSS transitions tell the browser to animate between two states instead of switching instantly. You define them on the base state, not the hover state, so that both hovering in and hovering out are animated.
- In style.css, scroll up to the .node rule (not .node:hover).
- Find this line near the end of the rule:
place-self: center;
}
- Add two new properties between place-self: center; and the closing }, so the end of your .node rule looks like this:
place-self: center;
transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
cursor: default;
}
Why list each property in the transition?
The transition property lists every CSS property you want to animate: background, border-color, box-shadow, and transform. Each one animates over 0.3 seconds with an ease curve that starts fast and slows down at the end.
You could use transition: all 0.3s ease as a shortcut, but listing properties individually gives you precise control and avoids accidentally animating properties you didn't intend to.
- Save style.css and refresh your browser.
- Hover over a card again. The glow, lift, and brightness changes now animate smoothly over 0.3 seconds instead of snapping instantly.
Still seeing an instant snap?
- Make sure the transition property is inside the .node rule, not inside .node:hover. Placing it on hover only animates the hover-in, not the hover-out.
- Check for typos in the property names. Each one must match exactly: background, border-color, box-shadow, transform.
My CSS transition isn't animating the hover effect smoothly.
Verify the full diagram
Your architecture diagram is complete. Time to check that everything works together.
- Refresh your browser (or open index.html if it is not already open).
- Check that six glass cards appear on a dark background with faint gradient orbs visible through the frosted glass.
- Hover over each card to confirm the glow, lift, and smooth 0.3-second transitions work.
- Look at the connector lines between nodes. The dashes should flow steadily in the direction of the arrowheads.
- Check the header at the top. You should see the Architecture badge, the System Overview title, and the Microservices / Production subtitle.
Something look off?
- If the glass effect looks invisible, check that your .orb divs are present in your HTML. Glassmorphism needs something behind the glass to refract.
- If the connectors don't align with the cards, make sure your .diagram has width: 800px and height: 560px. The SVG coordinates depend on these exact dimensions.
- If the dashes are not flowing, check that the @keyframes flow animation is present in your CSS and that each .connector has animation: flow 2s linear infinite.
My architecture diagram doesn't look right. Help me troubleshoot.
Awesome, I've got everything!
Great. Double check you've saved both index.html and style.css before moving on.
I'd like to double check the full code
Here is the complete style.css file. Compare it against yours to catch any differences.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #0a0a0c;
color: rgba(255, 255, 255, 0.9);
font-family: 'JetBrains Mono', monospace;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
/* ---- Page Layout ---- */
.page {
width: 100%;
max-width: 900px;
padding: 64px 32px;
}
/* ---- Header ---- */
.header {
text-align: center;
margin-bottom: 64px;
}
.badge {
display: inline-block;
font-size: 11px;
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
color: #5e6ad2;
background: rgba(94, 106, 210, 0.1);
border: 1px solid rgba(94, 106, 210, 0.2);
padding: 4px 12px;
border-radius: 100px;
margin-bottom: 16px;
}
.title {
font-size: 28px;
font-weight: 600;
letter-spacing: -0.02em;
margin-bottom: 8px;
}
.subtitle {
font-size: 13px;
color: rgba(255, 255, 255, 0.4);
}
/* ---- Diagram Container ---- */
.diagram {
position: relative;
width: 800px;
height: 560px;
margin: 0 auto;
}
/* ---- Background Orbs ---- */
.orb {
position: absolute;
border-radius: 50%;
filter: blur(80px);
pointer-events: none;
}
.orb-1 {
width: 300px;
height: 300px;
background: rgba(94, 106, 210, 0.08);
top: 10%;
left: 15%;
}
.orb-2 {
width: 250px;
height: 250px;
background: rgba(139, 92, 246, 0.06);
top: 50%;
right: 10%;
}
.orb-3 {
width: 200px;
height: 200px;
background: rgba(59, 130, 246, 0.05);
bottom: 5%;
left: 35%;
}
/* ---- SVG Connectors ---- */
.connectors {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
.connector {
stroke: rgba(255, 255, 255, 0.12);
stroke-width: 1;
stroke-dasharray: 6 4;
animation: flow 2s linear infinite;
}
.delay-1 { animation-delay: 0.3s; }
.delay-2 { animation-delay: 0.6s; }
.delay-3 { animation-delay: 0.9s; }
.delay-4 { animation-delay: 1.2s; }
@keyframes flow {
to {
stroke-dashoffset: -10;
}
}
/* ---- Node Grid ---- */
.nodes {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: repeat(4, 140px);
width: 100%;
height: 100%;
position: relative;
z-index: 2;
}
/* ---- Node Placement ---- */
.node-client { grid-column: 2; grid-row: 1; }
.node-gateway { grid-column: 2; grid-row: 2; }
.node-auth { grid-column: 1; grid-row: 3; }
.node-app { grid-column: 3; grid-row: 3; }
.node-cache { grid-column: 1; grid-row: 4; }
.node-db { grid-column: 3; grid-row: 4; }
/* ---- Node Card ---- */
.node {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 160px;
height: 80px;
background: rgba(255, 255, 255, 0.03);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 12px;
place-self: center;
transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s ease;
cursor: default;
}
.node:hover {
background: rgba(255, 255, 255, 0.06);
border-color: rgba(255, 255, 255, 0.12);
box-shadow: 0 0 40px rgba(94, 106, 210, 0.08);
transform: translateY(-2px);
}
.node-icon {
font-size: 10px;
color: rgba(94, 106, 210, 0.6);
margin-bottom: 4px;
}
.node-label {
font-size: 13px;
font-weight: 500;
color: rgba(255, 255, 255, 0.9);
}
.node-meta {
font-size: 10px;
color: rgba(255, 255, 255, 0.3);
margin-top: 2px;
}
And here is the complete index.html file for reference.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>System Architecture</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="page">
<header class="header">
<span class="badge">Architecture</span>
<h1 class="title">System Overview</h1>
<p class="subtitle">Microservices / Production</p>
</header>
<div class="diagram">
<div class="orb orb-1"></div>
<div class="orb orb-2"></div>
<div class="orb orb-3"></div>
<svg class="connectors" viewBox="0 0 800 560" fill="none">
<defs>
<marker id="arrowhead" viewBox="0 0 10 10" refX="9" refY="5"
markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M1,1 L9,5 L1,9" stroke="rgba(255,255,255,0.3)" stroke-width="1.5" fill="none" />
</marker>
</defs>
<path class="connector" d="M400,110 L400,170" marker-end="url(#arrowhead)" />
<path class="connector delay-1" d="M400,250 C400,280 133,280 133,310" marker-end="url(#arrowhead)" />
<path class="connector delay-2" d="M400,250 C400,280 667,280 667,310" marker-end="url(#arrowhead)" />
<path class="connector delay-3" d="M667,390 C667,420 133,420 133,450" marker-end="url(#arrowhead)" />
<path class="connector delay-4" d="M667,390 L667,450" marker-end="url(#arrowhead)" />
</svg>
<div class="nodes">
<div class="node node-client">
<span class="node-icon">◇</span>
<span class="node-label">Client</span>
<span class="node-meta">React SPA</span>
</div>
<div class="node node-gateway">
<span class="node-icon">⬡</span>
<span class="node-label">API Gateway</span>
<span class="node-meta">Kong</span>
</div>
<div class="node node-auth">
<span class="node-icon">◆</span>
<span class="node-label">Auth Service</span>
<span class="node-meta">JWT + OAuth 2.0</span>
</div>
<div class="node node-app">
<span class="node-icon">◆</span>
<span class="node-label">App Service</span>
<span class="node-meta">Node.js</span>
</div>
<div class="node node-cache">
<span class="node-icon">◈</span>
<span class="node-label">Cache</span>
<span class="node-meta">Redis</span>
</div>
<div class="node node-db">
<span class="node-icon">◈</span>
<span class="node-label">Database</span>
<span class="node-meta">PostgreSQL</span>
</div>
</div>
</div>
</div>
</body>
</html>
Secret mission
Add Pulsing Status Indicators and CSS-Only Tooltips
Your architecture diagram looks great. But real production dashboards show service health at a glance. Can you add pulsing status indicators and hover tooltips using nothing but CSS?
Before you go
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally with static files, so there are no ongoing costs.
Resources you used:
- glass-diagram project folder.
- index.html file.
- style.css file.
Keep everything running
No action needed. Choose this if you're still actively building or plan to extend the diagram with the Secret Mission.
- Your glass-diagram folder stays on your machine, ready to open in your browser anytime.
- Since these are static files with no running processes, there is nothing to shut down.
Pause - I'll come back to this later
Since this project is just two static files, there are no running processes to pause. Your files will sit quietly in your file system until you're ready to pick them back up.
- Close any browser tabs that have index.html open.
- Close the glass-diagram folder in Cursor.
- When you're ready to resume, open the folder in Cursor again and open index.html in your browser.
Delete - I don't want to use this again
Remove all project files from your machine. You can always rebuild from scratch using this project guide.
- Close any browser tabs that have index.html open.
- Close the glass-diagram folder in Cursor.
- Delete the entire glass-diagram folder from your file system.
Mission accomplished
That's a Wrap!
Nice work! You just built a production-quality architecture diagram using nothing but HTML and CSS.
You've learned how to:
- Create glassmorphic cards using backdrop-filter: blur(), translucent backgrounds, and subtle borders on a dark canvas with gradient orbs.
- Position a six-node system diagram using CSS Grid and draw animated SVG connectors with flowing dash animations and arrowhead markers.
- Add polished hover effects with glow, lift transitions, and smooth easing for a Linear-inspired aesthetic.
- Built pulsing status indicators and CSS-only tooltips that make the diagram interactive without a single line of JavaScript.
Ready to quiz yourself?