Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion css/screen.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
/* Light Theme - Kami Warm Parchment Canvas & Ink Blue Accent */
--bg-page: #f5f4ed; /* Warm parchment canvas */
--bg-card: #faf9f5; /* Warm ivory container */
--bg-card-rgb: 250, 249, 245;
--bg-subtle: #e8e6dc; /* Warm sand interactive surface */
--bg-code: #1c1c1a; /* Deep warm charcoal for code */
--border-color: #e8e6dc; /* Primary warm border */
Expand Down Expand Up @@ -43,6 +44,7 @@
/* Dark Theme - Kami Dark Surface & Warm Charcoal */
--bg-page: #141413; /* Deep dark warm background */
--bg-card: #242422; /* Dark warm surface */
--bg-card-rgb: 36, 36, 34;
--bg-subtle: #2e2e2a; /* Dark warm sand */
--bg-code: #0f0f0e; /* Dark code background */
--border-color: rgba(232, 230, 220, 0.12);
Expand Down Expand Up @@ -191,10 +193,26 @@ body {
.hero-banner {
position: relative;
text-align: center;
padding: 5.5rem 0 5rem 0;
padding: 4rem 1rem;
margin-bottom: 2.5rem;
overflow: hidden;
border-radius: var(--radius-lg);
background: var(--bg-card);
border: 1px solid var(--border-color);
}

.hero-content {
position: relative;
z-index: 2;
max-width: 680px;
margin: 0 auto;
padding: 1.25rem 1.75rem;
background: rgba(var(--bg-card-rgb, 250, 249, 245), 0.85);
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.03);
}

.hero-pretext-canvas {
Expand Down
4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

<section class="hero-banner">
<canvas id="hero-pretext-canvas" class="hero-pretext-canvas"></canvas>
<div class="hero-content pretext-obstacle">
<h1 class="hero-title" style="margin-bottom: 0.75rem;">Infrastructure, Systems & Engineering</h1>
<p class="hero-subtitle" style="margin: 0 auto;">Notes on Kubernetes, DevOps, Cloud Infrastructure, and Software Architecture.</p>
</div>
</section>
<script src="/js/dragon.js?v={{ site.time | date: '%s' }}" defer></script>
<script src="/js/hero-pretext.js?v={{ site.time | date: '%s' }}" defer></script>
Expand Down
63 changes: 56 additions & 7 deletions js/hero-pretext.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,28 +122,44 @@
const lineHeight = 22;
let wordIdx = 0;

// Build list of obstacle circles from dragon segments
const obstacles = [];
// Build list of obstacles (dragon circles + DOM element rectangles)
const circularObstacles = [];
if (dragon && dragon.segments) {
// Head obstacle
const head = dragon.segments[0];
obstacles.push({ x: head.x, y: head.y, r: 48 });
circularObstacles.push({ x: head.x, y: head.y, r: 48 });

// Body segment obstacles (every 3rd segment)
for (let i = 2; i < dragon.segments.length; i += 3) {
const seg = dragon.segments[i];
obstacles.push({ x: seg.x, y: seg.y, r: Math.max(16, seg.width * 0.6) });
circularObstacles.push({ x: seg.x, y: seg.y, r: Math.max(16, seg.width * 0.6) });
}
}

// Query DOM obstacles inside container
const domObstacles = [];
const containerRect = container.getBoundingClientRect();
const obstacleElements = container.querySelectorAll('.pretext-obstacle, .hero-title, .hero-subtitle');
obstacleElements.forEach(el => {
const rect = el.getBoundingClientRect();
const pad = 12; // padding around UI elements
const top = rect.top - containerRect.top - pad;
const bottom = rect.bottom - containerRect.top + pad;
const left = rect.left - containerRect.left - pad;
const right = rect.right - containerRect.left + pad;
if (right > 0 && left < width && bottom > 0 && top < height) {
domObstacles.push({ left, right, top, bottom });
}
});

for (let y = 18; y < height; y += lineHeight) {
let x = 12;

while (x < width - 12) {
let availableSpan = width - 12 - x;

// Subtract dragon segment obstacles intersecting line at y
for (let obs of obstacles) {
// 1. Subtract dragon circular segment obstacles intersecting line at y
for (let obs of circularObstacles) {
const dy = Math.abs(y - obs.y);
if (dy < obs.r) {
const dx = Math.sqrt(obs.r * obs.r - dy * dy);
Expand All @@ -161,6 +177,20 @@
}
}

// 2. Subtract DOM rectangular obstacles intersecting line at y
for (let obs of domObstacles) {
if (y >= obs.top && y <= obs.bottom) {
if (x < obs.right && x + availableSpan > obs.left) {
if (x < obs.left) {
availableSpan = obs.left - x;
} else {
x = obs.right;
availableSpan = width - 12 - x;
}
}
}
}

let count = 0;
let currentSpan = 0;
let checkIdx = wordIdx;
Expand All @@ -173,7 +203,26 @@
}

if (count === 0) {
x += 16;
// If phrase doesn't fit in gap, try CJK/character sub-chunk fitting
const phrase = phraseStream[wordIdx];
let subFit = "";
let subWidth = 0;
for (let char of phrase) {
const charWidth = ctx.measureText(char).width;
if (subWidth + charWidth <= availableSpan) {
subFit += char;
subWidth += charWidth;
} else {
break;
}
}

if (subFit.length > 0 && availableSpan > 20) {
ctx.fillText(subFit, x, y);
x += subWidth + 8;
} else {
x += 16;
}
continue;
}

Expand Down