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
4 changes: 4 additions & 0 deletions docs/hugo.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ params:
# Site metadata
description: "actsense is an open-source workflow security auditor. It maps every dependency a CI workflow runs and explains what each one exposes. GitHub Actions is supported today."
author: "actsense"

# Social preview (og:image, twitter:image). Source: scripts/og/og.html
images:
- "og.png"

# Navbar
navbar:
Expand Down
137 changes: 137 additions & 0 deletions docs/scripts/og/og.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<!DOCTYPE html>
<!-- Source for static/og.png (1200x630). Render with scripts/og/render.py. -->
<html lang="en">
<head>
<meta charset="utf-8">
<title>actsense OG image</title>
<link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400..700&family=Google+Sans+Code:wght@400..600&family=Stack+Sans+Notch:wght@200..700&display=block" rel="stylesheet">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { width: 1200px; height: 630px; overflow: hidden; }
body {
position: relative;
background: #0c0e12;
color: #f3f4f6;
font-family: "Google Sans", system-ui, sans-serif;
}

/* Same letter field as the homepage hero, frozen on one frame. */
canvas {
position: absolute;
inset: 0;
width: 1200px;
height: 630px;
}

/* Same horizon band as the homepage, tilted across the lower right. */
.horizon {
position: absolute;
left: -10%;
right: -10%;
top: 300px;
height: 460px;
transform: skewY(-7deg);
background: linear-gradient(180deg,
transparent 0%,
rgba(13, 59, 110, 0.55) 22%,
rgba(20, 78, 140, 0.9) 36%,
#e8491d 54%,
#ff7a2e 62%,
#ffd29a 70%,
rgba(255, 236, 214, 0.9) 74%,
transparent 92%);
-webkit-mask-image: linear-gradient(90deg, rgba(0, 0, 0, 0.12) 0%, rgba(0, 0, 0, 0.5) 45%, #000 80%);
}
.horizon::after {
content: '';
position: absolute;
inset: 0;
opacity: 0.75;
mix-blend-mode: soft-light;
-webkit-mask-image: linear-gradient(180deg, transparent 12%, #000 36%, #000 72%, transparent 90%);
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

.content {
position: absolute;
left: 88px;
top: 92px;
width: 760px;
}
.eyebrow {
display: inline-block;
padding: 8px 18px;
font-size: 21px;
font-weight: 500;
color: #d1d5db;
background: #111318;
border: 1px solid #262a33;
border-radius: 999px;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}
h1 {
margin-top: 26px;
font-family: "Stack Sans Notch", sans-serif;
font-size: 150px;
font-weight: 650;
line-height: 1;
letter-spacing: -0.03em;
}
.lede {
margin-top: 26px;
font-size: 30px;
line-height: 1.4;
color: #9ca3af;
text-wrap: balance;
}
.url {
position: absolute;
left: 88px;
bottom: 64px;
font-family: "Google Sans Code", ui-monospace, monospace;
font-size: 24px;
font-weight: 500;
color: #f3f4f6;
letter-spacing: 0.01em;
}
</style>
</head>
<body>
<canvas width="2400" height="1260"></canvas>
<div class="horizon"></div>
<div class="content">
<span class="eyebrow">Open-source Workflow Security Auditor</span>
<h1>actsense</h1>
<p class="lede">See every dependency your CI workflows run, at the version they run, and what each one exposes.</p>
</div>
<div class="url">actsense.dev</div>

<script>
// Mirrors static/hero-letters.js (dark theme), with the quiet zone moved
// behind the left-aligned text block.
document.fonts.ready.then(function () {
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const W = 1200, H = 630, STEP = 22, CHARS = 'ACTSENSE';
const t = 5200;
ctx.scale(2, 2);
ctx.font = '12px "Google Sans Code", ui-monospace, monospace';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const cx = 420, cy = 250;
for (let y = STEP / 2, row = 0; y < H; y += STEP, row++) {
for (let x = STEP / 2, col = 0; x < W; x += STEP, col++) {
const wave = Math.sin(x / 140 - t / 1400) * Math.cos(y / 90 + t / 2100);
const light = Math.max(0, wave) * 0.34 + 0.035;
const away = Math.min(1, Math.hypot((x - cx) / 560, (y - cy) / 300));
const alpha = light * away;
if (alpha < 0.012) continue;
ctx.fillStyle = 'rgba(245, 245, 244,' + alpha.toFixed(3) + ')';
ctx.fillText(CHARS[(row + col) % CHARS.length], x, y);
}
}
document.body.dataset.ready = '1';
});
</script>
</body>
</html>
28 changes: 28 additions & 0 deletions docs/scripts/og/render.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""Render og.html to static/og.png at 1200x630.

python3 docs/scripts/og/render.py

Needs Playwright (`pip install playwright && playwright install chromium`)
and network access for Google Fonts.
"""
from pathlib import Path

from playwright.sync_api import sync_playwright

HERE = Path(__file__).resolve().parent
OUT = HERE.parent.parent / "static" / "og.png"


def main() -> None:
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page(viewport={"width": 1200, "height": 630})
page.goto((HERE / "og.html").as_uri())
page.wait_for_selector("body[data-ready='1']")
page.screenshot(path=str(OUT))
browser.close()
print(f"wrote {OUT}")


if __name__ == "__main__":
main()
Binary file added docs/static/og.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading