Adding button generator

This commit is contained in:
Dan 2026-01-08 12:04:45 +00:00
parent d22d127cac
commit bdee635df0
11 changed files with 1458 additions and 6 deletions

View file

@ -0,0 +1,594 @@
(function () {
const canvas = document.getElementById("button-canvas");
const ctx = canvas.getContext("2d");
// Preload all web fonts for canvas rendering
const fonts = [
"Lato",
"Roboto",
"Open Sans",
"Montserrat",
"Oswald",
"Bebas Neue",
"Roboto Mono",
"VT323",
"Press Start 2P",
"DSEG7-Classic",
];
// Load fonts using CSS Font Loading API
const fontPromises = fonts.flatMap((font) => [
document.fonts.load(`400 12px "${font}"`),
document.fonts.load(`700 12px "${font}"`),
document.fonts.load(`italic 400 12px "${font}"`),
]);
Promise.all(fontPromises).then(() => {
console.log("All fonts loaded for canvas");
drawButton();
});
// Collapsible sections functionality
document.querySelectorAll(".control-group-header").forEach((header) => {
header.addEventListener("click", () => {
const controlGroup = header.closest(".control-group");
controlGroup.classList.toggle("collapsed");
});
});
// Get all controls
const controls = {
text: document.getElementById("button-text"),
textEnabled: document.getElementById("text-enabled"),
fontSize: document.getElementById("font-size"),
textX: document.getElementById("text-x"),
textY: document.getElementById("text-y"),
textColorType: document.getElementById("text-color-type"),
textColor: document.getElementById("text-color"),
textGradientColor1: document.getElementById("text-gradient-color1"),
textGradientColor2: document.getElementById("text-gradient-color2"),
textGradientAngle: document.getElementById("text-gradient-angle"),
textOutline: document.getElementById("text-outline"),
outlineColor: document.getElementById("outline-color"),
fontFamily: document.getElementById("font-family"),
fontBold: document.getElementById("font-bold"),
fontItalic: document.getElementById("font-italic"),
text2: document.getElementById("button-text2"),
text2Enabled: document.getElementById("text2-enabled"),
fontSize2: document.getElementById("font-size2"),
text2X: document.getElementById("text2-x"),
text2Y: document.getElementById("text2-y"),
text2ColorType: document.getElementById("text2-color-type"),
text2Color: document.getElementById("text2-color"),
text2GradientColor1: document.getElementById("text2-gradient-color1"),
text2GradientColor2: document.getElementById("text2-gradient-color2"),
text2GradientAngle: document.getElementById("text2-gradient-angle"),
text2Outline: document.getElementById("text2-outline"),
outline2Color: document.getElementById("outline2-color"),
fontFamily2: document.getElementById("font-family2"),
fontBold2: document.getElementById("font-bold2"),
fontItalic2: document.getElementById("font-italic2"),
bgType: document.getElementById("bg-type"),
bgColor: document.getElementById("bg-color"),
gradientColor1: document.getElementById("gradient-color1"),
gradientColor2: document.getElementById("gradient-color2"),
gradientAngle: document.getElementById("gradient-angle"),
textureType: document.getElementById("texture-type"),
textureColor1: document.getElementById("texture-color1"),
textureColor2: document.getElementById("texture-color2"),
textureScale: document.getElementById("texture-scale"),
borderWidth: document.getElementById("border-width"),
borderColor: document.getElementById("border-color"),
borderStyle: document.getElementById("border-style"),
};
// Update value displays
const updateValueDisplay = (id, value) => {
const display = document.getElementById(id + "-value");
if (display) display.textContent = value;
};
// Show/hide controls based on background type
controls.bgType.addEventListener("change", () => {
const solidControls = document.getElementById("solid-controls");
const gradientControls = document.getElementById("gradient-controls");
const textureControls = document.getElementById("texture-controls");
solidControls.style.display = "none";
gradientControls.style.display = "none";
textureControls.style.display = "none";
if (controls.bgType.value === "solid") {
solidControls.style.display = "block";
} else if (controls.bgType.value === "gradient") {
gradientControls.style.display = "block";
} else if (controls.bgType.value === "texture") {
textureControls.style.display = "block";
}
drawButton();
});
// Show/hide text color controls
controls.textColorType.addEventListener("change", () => {
const textSolidColor = document.getElementById("text-solid-color");
const textGradientColor = document.getElementById("text-gradient-color");
if (controls.textColorType.value === "solid") {
textSolidColor.style.display = "block";
textGradientColor.style.display = "none";
} else {
textSolidColor.style.display = "none";
textGradientColor.style.display = "block";
}
drawButton();
});
controls.text2ColorType.addEventListener("change", () => {
const text2SolidColor = document.getElementById("text2-solid-color");
const text2GradientColor = document.getElementById("text2-gradient-color");
if (controls.text2ColorType.value === "solid") {
text2SolidColor.style.display = "block";
text2GradientColor.style.display = "none";
} else {
text2SolidColor.style.display = "none";
text2GradientColor.style.display = "block";
}
drawButton();
});
// Show/hide outline color
controls.textOutline.addEventListener("change", () => {
controls.outlineColor.style.display = controls.textOutline.checked
? "block"
: "none";
drawButton();
});
controls.text2Outline.addEventListener("change", () => {
controls.outline2Color.style.display = controls.text2Outline.checked
? "block"
: "none";
drawButton();
});
// Draw texture patterns
function drawTexture(type, color1, color2, scale) {
const tempCanvas = document.createElement("canvas");
tempCanvas.width = 88;
tempCanvas.height = 31;
const tempCtx = tempCanvas.getContext("2d");
tempCtx.fillStyle = color1;
tempCtx.fillRect(0, 0, 88, 31);
tempCtx.fillStyle = color2;
const size = Math.max(2, Math.floor(scale / 10));
switch (type) {
case "dots":
for (let y = 0; y < 31; y += size * 2) {
for (let x = 0; x < 88; x += size * 2) {
tempCtx.beginPath();
tempCtx.arc(x, y, size / 2, 0, Math.PI * 2);
tempCtx.fill();
}
}
break;
case "grid":
for (let x = 0; x < 88; x += size) {
tempCtx.fillRect(x, 0, 1, 31);
}
for (let y = 0; y < 31; y += size) {
tempCtx.fillRect(0, y, 88, 1);
}
break;
case "diagonal":
for (let i = -31; i < 88; i += size) {
tempCtx.fillRect(i, 0, 2, 31);
tempCtx.save();
tempCtx.translate(i + 1, 0);
tempCtx.rotate(Math.PI / 4);
tempCtx.fillRect(0, 0, 2, 100);
tempCtx.restore();
}
break;
case "checkerboard":
for (let y = 0; y < 31; y += size) {
for (let x = 0; x < 88; x += size) {
if ((x / size + y / size) % 2 === 0) {
tempCtx.fillRect(x, y, size, size);
}
}
}
break;
case "noise":
for (let y = 0; y < 31; y++) {
for (let x = 0; x < 88; x++) {
if (Math.random() > 0.5) {
tempCtx.fillRect(x, y, 1, 1);
}
}
}
break;
case "stars":
for (let i = 0; i < scale; i++) {
const x = Math.floor(Math.random() * 88);
const y = Math.floor(Math.random() * 31);
tempCtx.fillRect(x, y, 1, 1);
tempCtx.fillRect(x - 1, y, 1, 1);
tempCtx.fillRect(x + 1, y, 1, 1);
tempCtx.fillRect(x, y - 1, 1, 1);
tempCtx.fillRect(x, y + 1, 1, 1);
}
break;
}
return tempCanvas;
}
// Helper function to draw to a single context
function drawToContext(context) {
context.clearRect(0, 0, 88, 31);
// Draw background
if (controls.bgType.value === "solid") {
context.fillStyle = controls.bgColor.value;
context.fillRect(0, 0, 88, 31);
} else if (controls.bgType.value === "gradient") {
const angle = parseFloat(controls.gradientAngle.value) * (Math.PI / 180);
const x1 = 44 + Math.cos(angle) * 44;
const y1 = 15.5 + Math.sin(angle) * 15.5;
const x2 = 44 - Math.cos(angle) * 44;
const y2 = 15.5 - Math.sin(angle) * 15.5;
const gradient = context.createLinearGradient(x1, y1, x2, y2);
gradient.addColorStop(0, controls.gradientColor1.value);
gradient.addColorStop(1, controls.gradientColor2.value);
context.fillStyle = gradient;
context.fillRect(0, 0, 88, 31);
} else if (controls.bgType.value === "texture") {
const texture = drawTexture(
controls.textureType.value,
controls.textureColor1.value,
controls.textureColor2.value,
parseFloat(controls.textureScale.value),
);
context.drawImage(texture, 0, 0);
}
// Draw border
const borderWidth = parseFloat(controls.borderWidth.value);
if (borderWidth > 0) {
const style = controls.borderStyle.value;
if (style === "solid") {
context.strokeStyle = controls.borderColor.value;
context.lineWidth = borderWidth;
context.strokeRect(
borderWidth / 2,
borderWidth / 2,
88 - borderWidth,
31 - borderWidth,
);
} else if (style === "inset" || style === "outset") {
const light = style === "outset";
context.strokeStyle = light ? "#ffffff" : "#000000";
context.lineWidth = borderWidth;
context.beginPath();
context.moveTo(0, 31);
context.lineTo(0, 0);
context.lineTo(88, 0);
context.stroke();
context.strokeStyle = light ? "#000000" : "#ffffff";
context.beginPath();
context.moveTo(88, 0);
context.lineTo(88, 31);
context.lineTo(0, 31);
context.stroke();
} else if (style === "ridge") {
context.strokeStyle = "#ffffff";
context.lineWidth = borderWidth / 2;
context.strokeRect(
borderWidth / 4,
borderWidth / 4,
88 - borderWidth / 2,
31 - borderWidth / 2,
);
context.strokeStyle = "#000000";
context.strokeRect(
(borderWidth * 3) / 4,
(borderWidth * 3) / 4,
88 - borderWidth * 1.5,
31 - borderWidth * 1.5,
);
}
}
// Draw text line 1
const text = controls.text.value;
if (text && controls.textEnabled.checked) {
const fontSize = parseFloat(controls.fontSize.value);
const fontWeight = controls.fontBold.checked ? "bold" : "normal";
const fontStyle = controls.fontItalic.checked ? "italic" : "normal";
const fontFamily = controls.fontFamily.value;
context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`;
context.textAlign = "center";
context.textBaseline = "middle";
const x = (parseFloat(controls.textX.value) / 100) * 88;
const y = (parseFloat(controls.textY.value) / 100) * 31;
if (controls.textOutline.checked) {
context.strokeStyle = controls.outlineColor.value;
context.lineWidth = 2;
context.strokeText(text, x, y);
}
// Apply text color or gradient
if (controls.textColorType.value === "solid") {
context.fillStyle = controls.textColor.value;
} else {
const angle =
parseFloat(controls.textGradientAngle.value) * (Math.PI / 180);
const textWidth = context.measureText(text).width;
const x1 = x - textWidth / 2 + (Math.cos(angle) * textWidth) / 2;
const y1 = y - fontSize / 2 + (Math.sin(angle) * fontSize) / 2;
const x2 = x + textWidth / 2 - (Math.cos(angle) * textWidth) / 2;
const y2 = y + fontSize / 2 - (Math.sin(angle) * fontSize) / 2;
const textGradient = context.createLinearGradient(x1, y1, x2, y2);
textGradient.addColorStop(0, controls.textGradientColor1.value);
textGradient.addColorStop(1, controls.textGradientColor2.value);
context.fillStyle = textGradient;
}
context.fillText(text, x, y);
}
// Draw text line 2
const text2 = controls.text2.value;
if (text2 && controls.text2Enabled.checked) {
const fontSize2 = parseFloat(controls.fontSize2.value);
const fontWeight2 = controls.fontBold2.checked ? "bold" : "normal";
const fontStyle2 = controls.fontItalic2.checked ? "italic" : "normal";
const fontFamily2 = controls.fontFamily2.value;
context.font = `${fontStyle2} ${fontWeight2} ${fontSize2}px "${fontFamily2}"`;
context.textAlign = "center";
context.textBaseline = "middle";
const x2 = (parseFloat(controls.text2X.value) / 100) * 88;
const y2 = (parseFloat(controls.text2Y.value) / 100) * 31;
if (controls.text2Outline.checked) {
context.strokeStyle = controls.outline2Color.value;
context.lineWidth = 2;
context.strokeText(text2, x2, y2);
}
// Apply text color or gradient
if (controls.text2ColorType.value === "solid") {
context.fillStyle = controls.text2Color.value;
} else {
const angle2 =
parseFloat(controls.text2GradientAngle.value) * (Math.PI / 180);
const text2Width = context.measureText(text2).width;
const x1_2 = x2 - text2Width / 2 + (Math.cos(angle2) * text2Width) / 2;
const y1_2 = y2 - fontSize2 / 2 + (Math.sin(angle2) * fontSize2) / 2;
const x2_2 = x2 + text2Width / 2 - (Math.cos(angle2) * text2Width) / 2;
const y2_2 = y2 + fontSize2 / 2 - (Math.sin(angle2) * fontSize2) / 2;
const text2Gradient = context.createLinearGradient(
x1_2,
y1_2,
x2_2,
y2_2,
);
text2Gradient.addColorStop(0, controls.text2GradientColor1.value);
text2Gradient.addColorStop(1, controls.text2GradientColor2.value);
context.fillStyle = text2Gradient;
}
context.fillText(text2, x2, y2);
}
}
// Main draw function
function drawButton() {
drawToContext(ctx);
}
// Download function
document.getElementById("download-button").addEventListener("click", () => {
const link = document.createElement("a");
link.download = "button-88x31.png";
link.href = canvas.toDataURL();
link.click();
});
// Preset buttons
document.getElementById("preset-random").addEventListener("click", () => {
const randomColor = () =>
"#" +
Math.floor(Math.random() * 16777215)
.toString(16)
.padStart(6, "0");
// Random background
controls.bgType.value = ["solid", "gradient", "texture"][
Math.floor(Math.random() * 3)
];
controls.bgColor.value = randomColor();
controls.gradientColor1.value = randomColor();
controls.gradientColor2.value = randomColor();
controls.gradientAngle.value = Math.floor(Math.random() * 360);
controls.textureColor1.value = randomColor();
controls.textureColor2.value = randomColor();
controls.textureType.value = [
"dots",
"grid",
"diagonal",
"checkerboard",
"noise",
"stars",
][Math.floor(Math.random() * 6)];
// Random text 1 color (50% chance of gradient)
controls.textColorType.value = Math.random() > 0.5 ? "gradient" : "solid";
controls.textColor.value = randomColor();
controls.textGradientColor1.value = randomColor();
controls.textGradientColor2.value = randomColor();
controls.textGradientAngle.value = Math.floor(Math.random() * 360);
// Random text 2 color (50% chance of gradient)
controls.text2ColorType.value = Math.random() > 0.5 ? "gradient" : "solid";
controls.text2Color.value = randomColor();
controls.text2GradientColor1.value = randomColor();
controls.text2GradientColor2.value = randomColor();
controls.text2GradientAngle.value = Math.floor(Math.random() * 360);
// Random border
controls.borderColor.value = randomColor();
controls.borderWidth.value = Math.floor(Math.random() * 6);
controls.borderStyle.value = ["solid", "inset", "outset", "ridge"][
Math.floor(Math.random() * 4)
];
// Random text styles
controls.fontBold.checked = Math.random() > 0.5;
controls.fontItalic.checked = Math.random() > 0.5;
controls.fontBold2.checked = Math.random() > 0.5;
controls.fontItalic2.checked = Math.random() > 0.5;
// Update displays
updateValueDisplay("gradient-angle", controls.gradientAngle.value);
updateValueDisplay("text-gradient-angle", controls.textGradientAngle.value);
updateValueDisplay(
"text2-gradient-angle",
controls.text2GradientAngle.value,
);
updateValueDisplay("border-width", controls.borderWidth.value);
controls.bgType.dispatchEvent(new Event("change"));
controls.textColorType.dispatchEvent(new Event("change"));
controls.text2ColorType.dispatchEvent(new Event("change"));
drawButton();
});
document.getElementById("preset-classic").addEventListener("click", () => {
// Classic 90s web button style
controls.bgType.value = "gradient";
controls.gradientColor1.value = "#0066cc";
controls.gradientColor2.value = "#0099ff";
controls.gradientAngle.value = 90;
controls.textColorType.value = "solid";
controls.textColor.value = "#ffffff";
controls.text2ColorType.value = "solid";
controls.text2Color.value = "#ffffff";
controls.borderWidth.value = 2;
controls.borderColor.value = "#000000";
controls.borderStyle.value = "outset";
controls.fontFamily.value = "Oswald";
controls.fontFamily2.value = "Lato";
controls.fontBold.checked = true;
controls.fontBold2.checked = false;
controls.fontItalic.checked = false;
controls.fontItalic2.checked = false;
controls.text.value = "RITUAL.SH";
controls.text2.value = "FREE THE WEB";
controls.textEnabled.checked = true;
controls.text2Enabled.checked = true;
controls.fontSize.value = 12;
controls.fontSize2.value = 8;
controls.textY.value = 35;
controls.text2Y.value = 65;
updateValueDisplay("font-size", 12);
updateValueDisplay("font-size2", 8);
updateValueDisplay("gradient-angle", 90);
updateValueDisplay("text-y", 35);
updateValueDisplay("text2-y", 65);
controls.bgType.dispatchEvent(new Event("change"));
controls.textColorType.dispatchEvent(new Event("change"));
controls.text2ColorType.dispatchEvent(new Event("change"));
drawButton();
});
document.getElementById("preset-modern").addEventListener("click", () => {
// Modern cyberpunk style with gradient text
controls.bgType.value = "gradient";
controls.gradientColor1.value = "#0a0a0a";
controls.gradientColor2.value = "#1a0a2e";
controls.gradientAngle.value = 135;
controls.textColorType.value = "gradient";
controls.textGradientColor1.value = "#00ffaa";
controls.textGradientColor2.value = "#00ffff";
controls.textGradientAngle.value = 90;
controls.text2ColorType.value = "gradient";
controls.text2GradientColor1.value = "#ff00ff";
controls.text2GradientColor2.value = "#ff6600";
controls.text2GradientAngle.value = 0;
controls.borderWidth.value = 1;
controls.borderColor.value = "#00ffaa";
controls.borderStyle.value = "solid";
controls.fontFamily.value = "Roboto Mono";
controls.fontFamily2.value = "Roboto Mono";
controls.fontBold.checked = true;
controls.fontBold2.checked = false;
controls.fontItalic.checked = false;
controls.fontItalic2.checked = false;
controls.text.value = "RITUAL.SH";
controls.text2.value = "EST. 2024";
controls.textEnabled.checked = true;
controls.text2Enabled.checked = true;
controls.fontSize.value = 11;
controls.fontSize2.value = 9;
controls.textY.value = 35;
controls.text2Y.value = 65;
updateValueDisplay("font-size", 11);
updateValueDisplay("font-size2", 9);
updateValueDisplay("gradient-angle", 135);
updateValueDisplay("text-gradient-angle", 90);
updateValueDisplay("text2-gradient-angle", 0);
updateValueDisplay("text-y", 35);
updateValueDisplay("text2-y", 65);
controls.textColorType.dispatchEvent(new Event("change"));
controls.text2ColorType.dispatchEvent(new Event("change"));
controls.bgType.dispatchEvent(new Event("change"));
drawButton();
});
// Add event listeners to all controls
Object.values(controls).forEach((control) => {
if (control) {
control.addEventListener("input", drawButton);
control.addEventListener("change", drawButton);
if (control.type === "range") {
control.addEventListener("input", (e) => {
updateValueDisplay(e.target.id, e.target.value);
});
}
}
});
// Initial draw
drawButton();
})();

View file

@ -0,0 +1,503 @@
@import url(https://fonts.bunny.net/css?family=bebas-neue:400|lato:400,400i,700,700i|montserrat:400,400i,700,700i|open-sans:400,400i,700,700i|oswald:400,700|press-start-2p:400|roboto:400,400i,700,700i|roboto-mono:400,400i,700,700i|vt323:400);
#button-generator-app {
margin: 2rem 0;
padding: 2rem;
background: linear-gradient(
145deg,
rgba(5, 15, 30, 0.9) 0%,
rgba(10, 20, 40, 0.95) 100%
);
border-radius: 6px;
border: 1px solid rgba(0, 150, 255, 0.4);
box-shadow:
0 0 30px rgba(0, 150, 255, 0.15),
0 0 50px rgba(255, 120, 0, 0.08),
inset 0 0 60px rgba(0, 100, 200, 0.05);
position: relative;
overflow: visible;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: linear-gradient(
90deg,
transparent,
rgba(0, 150, 255, 0.6),
rgba(255, 120, 0, 0.6),
transparent
);
}
.generator-container {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 2rem;
margin-top: 1rem;
position: relative;
z-index: 1;
@media (max-width: 768px) {
grid-template-columns: 1fr;
}
}
.preview-section {
background: linear-gradient(
135deg,
rgba(0, 100, 180, 0.15) 0%,
rgba(0, 80, 150, 0.1) 100%
);
padding: 1.5rem;
border-radius: 6px;
border: 1px solid rgba(0, 150, 255, 0.3);
display: flex;
flex-direction: column;
gap: 1rem;
position: sticky;
top: 1rem;
align-self: flex-start;
max-height: calc(100vh - 2rem);
overflow-y: auto;
box-shadow:
0 0 20px rgba(0, 150, 255, 0.1),
inset 0 0 40px rgba(0, 100, 200, 0.05);
&::before {
content: "";
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
background: linear-gradient(
180deg,
rgba(0, 150, 255, 0.7),
rgba(255, 120, 0, 0.5)
);
border-radius: 6px 0 0 6px;
}
h3 {
margin-top: 0;
color: #0096ff;
font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.1em;
text-shadow: 0 0 15px rgba(0, 150, 255, 0.5);
font-size: 1.2rem;
}
}
.presets-container {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 2px solid;
border-image: linear-gradient(
90deg,
transparent,
rgba(0, 150, 255, 0.5),
transparent
)
1;
h3 {
margin-top: 0;
margin-bottom: 1rem;
color: rgba(100, 180, 255, 0.9);
font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
text-shadow: 0 0 10px rgba(0, 150, 255, 0.4);
font-size: 1rem;
}
}
.preview-container {
display: flex;
flex-direction: column;
gap: 1rem;
}
.preview-wrapper {
background: repeating-conic-gradient(
rgba(20, 30, 50, 0.8) 0% 25%,
rgba(10, 20, 40, 0.9) 0% 50%
)
50% / 20px 20px;
padding: 2rem;
display: flex;
justify-content: center;
align-items: center;
border-radius: 4px;
border: 1px solid rgba(0, 150, 255, 0.2);
}
#button-canvas {
image-rendering: pixelated;
image-rendering: -moz-crisp-edges;
image-rendering: crisp-edges;
border: 1px solid rgba(0, 150, 255, 0.4);
box-shadow: 0 0 15px rgba(0, 150, 255, 0.2);
}
.controls-section {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.control-group {
background: linear-gradient(
135deg,
rgba(0, 100, 180, 0.15) 0%,
rgba(0, 80, 150, 0.1) 100%
);
padding: 1.5rem;
border-radius: 6px;
border: 1px solid rgba(0, 150, 255, 0.3);
box-shadow:
0 0 20px rgba(0, 150, 255, 0.1),
inset 0 0 40px rgba(0, 100, 200, 0.05);
position: relative;
&::before {
content: "";
position: absolute;
left: 0;
top: 0;
width: 3px;
height: 100%;
background: linear-gradient(
180deg,
rgba(0, 150, 255, 0.7),
rgba(255, 120, 0, 0.5)
);
opacity: 0;
transition: opacity 0.3s ease;
}
&:hover::before {
opacity: 1;
}
h3 {
margin-top: 0;
margin-bottom: 1rem;
}
label {
display: block;
margin-top: 0.75rem;
margin-bottom: 0.25rem;
font-weight: 500;
font-size: 0.85rem;
color: rgba(100, 180, 255, 0.8);
text-transform: uppercase;
letter-spacing: 0.05em;
}
input[type="text"],
select {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid rgba(0, 150, 255, 0.4);
border-radius: 4px;
font-size: 0.9rem;
background: rgba(5, 15, 30, 0.7);
color: rgba(200, 220, 255, 0.95);
transition: all 0.3s ease;
box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.3);
&:focus {
outline: none;
border-color: rgba(0, 150, 255, 0.7);
box-shadow:
0 0 15px rgba(0, 150, 255, 0.3),
inset 0 2px 8px rgba(0, 0, 0, 0.4);
background: rgba(5, 15, 30, 0.9);
}
}
select {
cursor: pointer;
option {
background: rgba(5, 15, 30, 0.95);
color: rgba(200, 220, 255, 0.95);
}
}
// Font preview in dropdowns
select#font-family,
select#font-family2 {
option[value="Lato"] {
font-family: "Lato", sans-serif;
}
option[value="Roboto"] {
font-family: "Roboto", sans-serif;
}
option[value="Open Sans"] {
font-family: "Open Sans", sans-serif;
}
option[value="Montserrat"] {
font-family: "Montserrat", sans-serif;
}
option[value="Oswald"] {
font-family: "Oswald", sans-serif;
}
option[value="Bebas Neue"] {
font-family: "Bebas Neue", display;
}
option[value="Roboto Mono"] {
font-family: "Roboto Mono", monospace;
}
option[value="VT323"] {
font-family: "VT323", monospace;
}
option[value="Press Start 2P"] {
font-family: "Press Start 2P", display;
}
}
input[type="range"] {
width: 100%;
margin-top: 0.25rem;
-webkit-appearance: none;
appearance: none;
background: rgba(0, 100, 180, 0.2);
border-radius: 4px;
height: 6px;
outline: none;
&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
background: linear-gradient(135deg, #0096ff, #66ccff);
cursor: pointer;
border-radius: 50%;
box-shadow: 0 0 10px rgba(0, 150, 255, 0.6);
border: 2px solid rgba(0, 150, 255, 0.8);
}
&::-moz-range-thumb {
width: 18px;
height: 18px;
background: linear-gradient(135deg, #0096ff, #66ccff);
cursor: pointer;
border-radius: 50%;
box-shadow: 0 0 10px rgba(0, 150, 255, 0.6);
border: 2px solid rgba(0, 150, 255, 0.8);
}
}
input[type="color"] {
width: 100%;
height: 50px;
border: 1px solid rgba(0, 150, 255, 0.4);
border-radius: 4px;
cursor: pointer;
background: rgba(5, 15, 30, 0.7);
padding: 4px;
transition: all 0.3s ease;
&:hover {
border-color: rgba(0, 150, 255, 0.7);
box-shadow: 0 0 15px rgba(0, 150, 255, 0.3);
}
}
input[type="checkbox"] {
width: auto;
margin-left: 0.5rem;
cursor: pointer;
accent-color: #0096ff;
}
}
.control-group-header {
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
user-select: none;
margin: 0;
padding: 0.5rem 0;
border-bottom: 2px solid;
border-image: linear-gradient(
90deg,
transparent,
rgba(0, 150, 255, 0.5),
transparent
)
1;
margin-bottom: 1rem;
color: rgba(100, 180, 255, 0.9);
font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.08em;
transition: all 0.3s ease;
&:hover {
color: #66ccff;
text-shadow: 0 0 10px rgba(0, 150, 255, 0.5);
}
}
.toggle-icon {
font-size: 1.5rem;
font-weight: bold;
transition: transform 0.3s ease;
color: rgba(0, 150, 255, 0.8);
.control-group.collapsed & {
transform: rotate(180deg);
}
}
.control-group-content {
overflow: hidden;
transition:
max-height 0.3s ease,
opacity 0.3s ease;
.control-group.collapsed & {
max-height: 0;
opacity: 0;
pointer-events: none;
}
}
.checkbox-row {
display: flex;
gap: 1.5rem;
margin-top: 0.75rem;
}
.checkbox-label {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
font-size: 0.85rem;
cursor: pointer;
margin: 0;
color: rgba(100, 180, 255, 0.8);
text-transform: uppercase;
letter-spacing: 0.05em;
transition: color 0.3s ease;
&:hover {
color: #66ccff;
}
input[type="checkbox"] {
margin: 0;
cursor: pointer;
}
span {
user-select: none;
}
}
.btn-primary,
.btn-secondary {
width: 100%;
padding: 0.75rem 1.5rem;
font-size: 0.95rem;
font-weight: 600;
border: 1px solid rgba(0, 150, 255, 0.5);
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.08em;
position: relative;
overflow: hidden;
&::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
rgba(0, 150, 255, 0.3),
transparent
);
transition: left 0.5s ease;
}
&:hover::before {
left: 100%;
}
}
.btn-primary {
background: linear-gradient(
135deg,
rgba(0, 120, 200, 0.3) 0%,
rgba(0, 100, 180, 0.2) 100%
);
color: #66ccff;
&:hover {
background: linear-gradient(
135deg,
rgba(0, 150, 255, 0.4) 0%,
rgba(255, 100, 0, 0.3) 100%
);
border-color: rgba(0, 150, 255, 0.8);
box-shadow:
0 0 20px rgba(0, 150, 255, 0.4),
0 0 30px rgba(255, 120, 0, 0.2);
color: #fff;
text-shadow: 0 0 10px rgba(0, 150, 255, 0.6);
transform: translateY(-2px);
}
&:active {
transform: translateY(0);
}
}
.btn-secondary {
background: linear-gradient(
135deg,
rgba(0, 100, 180, 0.2) 0%,
rgba(0, 80, 150, 0.15) 100%
);
color: #66ccff;
margin-top: 0.5rem;
&:hover {
background: linear-gradient(
135deg,
rgba(0, 120, 200, 0.3) 0%,
rgba(255, 100, 0, 0.2) 100%
);
border-color: rgba(0, 150, 255, 0.6);
transform: translateY(-2px);
box-shadow: 0 0 20px rgba(0, 150, 255, 0.3);
color: #fff;
text-shadow: 0 0 8px rgba(0, 150, 255, 0.5);
}
&:active {
transform: translateY(0);
}
}
}

View file

@ -244,6 +244,7 @@
inset 0 1px 2px rgba(255, 255, 255, 0.1);
border-radius: 1em;
cursor: pointer;
position: relative;
&::after {
content: "Interests and Tools";
@ -267,7 +268,7 @@
@include media-down(lg) {
margin-left: -0.5em;
opacity: 1;
bottom: 0;
bottom: -40px;
font-size: 20px;
}
}

View file

@ -84,7 +84,7 @@
max-width: 1400px;
margin: 0 auto;
position: relative;
z-index: 1;
z-index: 10;
}
// Portal Header with portals on either side
@ -97,7 +97,7 @@
position: relative;
.portal-title {
font-size: 3.5rem;
font-size: 2.5rem;
font-family: Verdana, "Trebuchet MS", Tahoma, sans-serif;
font-weight: 700;
text-transform: uppercase;
@ -108,11 +108,13 @@
0 0 40px rgba(255, 255, 255, 0.4),
0 2px 4px rgba(0, 0, 0, 0.3);
margin: 0;
margin-top: 50px;
position: relative;
z-index: 2;
@include media-up(md) {
font-size: 4rem;
margin-top: 0;
}
}
@ -192,7 +194,7 @@
left: 5%;
bottom: 5%;
width: 150px;
z-index: -1;
z-index: 1;
.heart-icon {
font-size: 2em;
@ -257,6 +259,7 @@
.portal-sign {
position: relative;
padding: 3rem 2rem;
z-index: 10;
border: 10px solid black;
background: white;
@ -579,7 +582,8 @@
inset 0 0 100px rgba(0, 100, 200, 0.05),
0 8px 32px rgba(0, 0, 0, 0.4);
position: relative;
overflow: hidden;
overflow: visible;
z-index: 10;
// Subtle tech panel grid pattern
&::before {

View file

@ -26,6 +26,7 @@
@import "pages/blog";
@import "pages/media";
@import "pages/resources";
@import "pages/button-generator";
@import url(https://fonts.bunny.net/css?family=abel:400|barlow-condensed:400,500|caveat:400|lato:300,300i,400,400i|neonderthaw:400);