diff --git a/src/content/links.json b/src/content/links.json
index effd818..ad67b7c 100644
--- a/src/content/links.json
+++ b/src/content/links.json
@@ -127,8 +127,8 @@
"group": 2
},
{
- "name": "source code",
- "href": "https://git.brulijam.com/brulijam/introduction-website",
+ "name": "source code (38c3 branch)",
+ "href": "https://git.brulijam.com/brulijam/introduction-website/src/branch/38c3-theme",
"group": "footer"
},
{
diff --git a/src/css/main.css b/src/css/main.css
index 13ddb22..3969538 100644
--- a/src/css/main.css
+++ b/src/css/main.css
@@ -1,22 +1,45 @@
/* Root Variables */
:root {
- --bg-color: rgb(15, 15, 15);
- --primary: rgb(240, 240, 240);
- --secondary-color: hsl(210, 80%, 80%);
- --secondary-color2: hsl(210, 60%, 40%);
- --secondary-color-bg: hsl(210, 40%, 5%);
--font-weight: 350;
- --font-family: "Lucida Console";
+ --font-family: 'space-mono';
--bigger-font-weight: 700;
--link-border-radius: 1000px;
--page-max-width: 700px;
+
+ --color-primary: #FF5053;
+ --color-highlight: #FEF2FF;
+ --color-accent-a: #B2AAFF;
+ --color-accent-b: #6A5FDB;
+ --color-accent-c: #29114C;
+ --color-accent-d: #261A66;
+ --color-accent-e: #190B2F;
+ --color-background: #0F000A;
+}
+
+@font-face {
+ font-family: 'pilowlava';
+ /*src: URL('../fonts/pilowlava/Fonts/webfonts/Pilowlava-Regular.woff2') format('woff2');*/
+ src: url('../fonts/pilowlava/Fonts/webfonts/Pilowlava-Regular.woff2') format('woff2');
+}
+@font-face {
+ font-family: 'spacegrotesk';
+ src: url('../fonts/space-grotesk-1.1.4/webfont/SpaceGrotesk-Regular.woff2') format('woff2');
+}
+
+@font-face {
+ font-family: 'space-mono';
+ src: url('../fonts/space-mono/SpaceMono-Regular.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'uncut-sans';
+ src: url('../fonts/uncut-sans/Webfonts/UncutSans-Regular.woff2') format('woff2');
}
/* General Styles */
body {
- background-color: var(--secondary-color-bg);
- color: var(--primary);
- /*color: var(--primary);*/
+ background-color: var(--color-background);
+ color: var(--color-accent-a);
font-family: var(--font-family), monospace;
font-weight: var(--font-weight);
overflow-x: hidden;
@@ -31,23 +54,59 @@ h1, p {
main {
position: relative;
- z-index: 1;
+ z-index: 2;
max-width: var(--page-max-width);
margin: 0 auto;
padding: 20px;
- filter: drop-shadow(0 0 2px var(--secondary-color)) brightness(1);
+ /*filter: drop-shadow(0 0 2px var(--color-primary)) brightness(1);*/
}
::selection {
- color: var(--bg-color);
- background: var(--secondary-color);
+ color: var(--color-background);
+ background: var(--color-primary);
}
+/* 38c3 */
+.blobs {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 1; /* Keep the container behind all content */
+ pointer-events: none; /* Prevent interaction */
+ overflow: hidden;
+}
+
+.blobs img {
+ pointer-events: none; /* Ensure the images don't block anything */
+ will-change: transform; /* Improve performance for animations */
+}
+
+.grid {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: -2; /* Ensure the grid is behind the blobs */
+ display: grid;
+ pointer-events: none; /* Prevent interaction */
+ opacity: 0.5;
+ background-image: linear-gradient(var(--color-accent-e) 1px, transparent 1px),
+ linear-gradient(90deg, var(--color-accent-e) 1px, transparent 1px);
+ background-size: 60px 60px; /* Adjust the grid spacing */
+ background-color: var(--color-background); /* Base color for the grid */
+}
+
+
/* Header Styles */
header {
text-align: center;
width: max-content;
margin: 0 auto;
+ /*background-color: var(--color-background);*/
+ /*border: 1px var(--color-accent-e) solid;*/
}
header .names {
@@ -57,7 +116,14 @@ header .names {
header h1, header p {
text-align: left;
- color: var(--secondary-color);
+ font-size: 35px;
+ color: var(--color-primary);
+ font-family: pilowlava, monospace;
+}
+
+header p {
+ font-family: space-mono, monospace;
+ font-size: 25px;
}
header img {
@@ -66,10 +132,24 @@ header img {
padding: 0;
}
+/* Description */
+header, .description, .linkGroup, .timer, footer {
+ background-color: rgba(15, 0, 10, 0.9);
+ /*border: 1px var(--color-primary) solid;*/
+ border: 1px var(--color-accent-c) solid;
+}
+
+header, .description, .linkGroup, .timer {
+ padding: 5px;
+}
+
/* Links Styles */
h2 {
width: 100%;
+ font-size: 30px;
margin: 0;
+ color: var(--color-accent-a);
+ font-family: pilowlava, monospace;
@media only screen and (max-width: 480px) {
text-align: center; /* slim */
@@ -77,7 +157,7 @@ h2 {
}
@media only screen and (min-width: 480px) {
- text-align: left; /* wide */
+ text-align: center; /* wide */
margin: 0;
}
}
@@ -91,7 +171,7 @@ h2 {
}
.text-link {
- color: var(--secondary-color);
+ color: var(--color-primary);
text-decoration: none;
position: relative;
padding: 5px;
@@ -100,13 +180,13 @@ h2 {
}
.text-link:hover {
- filter: drop-shadow(0 0 5px var(--secondary-color2)) brightness(1.02);
+ filter: drop-shadow(0 0 5px var(--color-primary)) brightness(1.02);
top: -2px;
}
/* Links Container */
.links {
- padding: 0 10px;
+ padding: 0 0;
}
/* Link Group */
@@ -119,6 +199,11 @@ h2 {
}
}
+.linkGroup * {
+ color: var(--color-accent-a);
+ border: none;
+}
+
/* Link Styles */
.link {
height: fit-content;
@@ -142,8 +227,8 @@ h2 {
text-align: center;
text-decoration: none;
text-transform: lowercase;
- color: var(--bg-color);
- background-color: var(--secondary-color);
+ color: var(--color-background);
+ background-color: var(--color-primary);
display: block;
box-sizing: border-box;
padding: 10px 20px;
@@ -152,6 +237,7 @@ h2 {
@media only screen and (min-width: 480px) {
width: auto; /* wide */
+ padding: 5px 20px;
}
}
@@ -166,7 +252,7 @@ h2 {
/* Default Hover Effect */
.hoverHighlight:hover {
@media only screen and (min-width: 480px) {
- filter: drop-shadow(0 0 8px var(--secondary-color)) brightness(1.1);
+ filter: drop-shadow(0 0 8px var(--color-primary)) brightness(1.1);
top: -2px;
}
}
@@ -178,7 +264,7 @@ h2 {
/* Search Highlighting */
.highlightSearch {
- filter: drop-shadow(0 0 8px var(--secondary-color)) brightness(1.1);
+ filter: drop-shadow(0 0 8px var(--color-primary)) brightness(1.1);
z-index: 1;
@media only screen and (min-width: 480px) {
top: -2px;
@@ -206,9 +292,9 @@ h2 {
}
#link-search {
- background-color: var(--secondary-color2);
- color: var(--secondary-color);
- border: 2px solid var(--secondary-color2);
+ background-color: var(--color-background);
+ color: var(--color-accent-a);
+ border: 2px solid var(--color-primary);
border-radius: 50px;
padding: 10px 15px;
font-size: 16px;
@@ -221,21 +307,21 @@ h2 {
}
#link-search::placeholder {
- color: var(--secondary-color);
+ color: var(--color-accent-a);
opacity: 0.9; /* Placeholder text opacity */
}
#link-search:focus::placeholder {
- color: var(--secondary-color);
+ color: var(--color-accent-e);
opacity: 0.9; /* Placeholder text opacity */
}
#link-search:focus {
outline: none;
- background-color: var(--secondary-color-bg);
- box-shadow: 0 0 10px var(--secondary-color);
- border-color: var(--secondary-color2);
- color: var(--secondary-color);
+ background-color: var(--color-background);
+ box-shadow: 0 0 10px var(--color-primary);
+ border-color: var(--color-primary);
+ color: var(--color-primary);
}
@media only screen and (max-width: 480px) {
@@ -252,6 +338,9 @@ h2 {
}
/* Timers Styles */
+.timer {
+}
+
.timer p {
@media only screen and (max-width: 480px) {
margin-bottom: 20px; /* slim */
@@ -259,7 +348,8 @@ h2 {
}
.timer p span {
- color: var(--secondary-color);
+ color: var(--color-primary);
+ /*color: var(--color-primary);*/
text-decoration: none;
}
diff --git a/src/index.html b/src/index.html
index 9fe2c1b..45cef41 100644
--- a/src/index.html
+++ b/src/index.html
@@ -6,10 +6,11 @@
Julian Brammer
-
+
+
diff --git a/src/js/background.js b/src/js/background.js
new file mode 100644
index 0000000..e1cbd01
--- /dev/null
+++ b/src/js/background.js
@@ -0,0 +1,105 @@
+function getRandomInt(min, max) {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+}
+
+function generateGridPositions(rows, cols) {
+ const positions = [];
+ for (let row = 0; row < rows; row++) {
+ for (let col = 0; col < cols; col++) {
+ const top = (row + 0.5) * (100 / rows);
+ const left = (col + 0.5) * (100 / cols);
+ positions.push({ top, left });
+ }
+ }
+ return positions;
+}
+
+function getRandomPosition(availablePositions) {
+ const index = Math.floor(Math.random() * availablePositions.length);
+ return availablePositions.splice(index, 1)[0];
+}
+
+function createBlob(blobContainer, type, fileName, zIndex, position) {
+ const blob = document.createElement('img');
+ blob.src = `assets/blobs/${fileName}`;
+ blob.classList.add(type);
+
+ const randomRotation = getRandomInt(0, 360);
+
+ // blob.style.top = `${Math.random() * 100}%`;
+ // blob.style.left = `${Math.random() * 100}%`;
+ blob.style.top = `${position.top}%`;
+ blob.style.left = `${position.left}%`;
+ blob.style.transform = `translate(-50%, -50%) rotate(${randomRotation}deg)`;
+ blob.style.position = 'absolute';
+ blob.style.maxWidth = '300%';
+ blob.style.opacity = type === 'line' ? '0.5' : '0.8';
+ blob.style.zIndex = zIndex;
+
+ blob.dataset.rotation = randomRotation;
+ blobContainer.appendChild(blob);
+ return blob;
+}
+
+function adjustBlobCount() {
+ const width = window.innerWidth;
+ const height = window.innerHeight;
+ const baseCount = 5;
+ const widthFactor = Math.max(1, Math.floor(width / 400));
+ const heightFactor = Math.max(1, Math.floor(height / 600));
+ const totalCount = Math.min(baseCount * widthFactor, baseCount * heightFactor);
+
+ const solidsCount = Math.max(1, Math.floor(totalCount * 0.5));
+ const linesCount = totalCount - solidsCount;
+
+ return { solidsCount, linesCount };
+}
+
+
+
+
+document.addEventListener('DOMContentLoaded', () => {
+ const grid = document.createElement('div');
+ grid.classList.add('grid');
+ document.body.prepend(grid);
+
+ const blobContainer = document.createElement('div');
+ blobContainer.classList.add('blobs');
+ document.body.prepend(blobContainer);
+
+ const { solidsCount, linesCount } = adjustBlobCount();
+ let zIndexCounter = 1;
+
+ const gridSize = Math.ceil(Math.sqrt(solidsCount + linesCount));
+ const positions = generateGridPositions(gridSize, gridSize);
+
+ for (let i = 0; i < solidsCount; i++) {
+ const x = getRandomInt(1, 13);
+ const position = getRandomPosition(positions);
+ createBlob(blobContainer,'solid', `blob-freeform-${x}_filled.svg`, zIndexCounter, position);
+ zIndexCounter += 2;
+ }
+
+ for (let i = 0; i < linesCount; i++) {
+ const x = getRandomInt(1, 13);
+ const position = getRandomPosition(positions);
+ const color = Math.random() > 0.5 ? '_red' : '';
+ createBlob(blobContainer, 'line', `blob-freeform-${x}${color}.svg`, zIndexCounter - 1, position);
+ }
+});
+
+
+document.addEventListener('mousemove', (event) => {
+ const elements = [...document.querySelectorAll('.solid, .line')];
+ const { clientX: mouseX, clientY: mouseY } = event;
+ const { innerWidth: width, innerHeight: height } = window;
+
+ const xOffset = (mouseX / width - 0.5) * 200;
+ const yOffset = (mouseY / height - 0.5) * 200;
+
+ elements.forEach((element, i) => {
+ const strength = element.classList.contains('solid') ? 5 + i * 3 : 1 + i * 0.5;
+ const rotation = element.dataset.rotation || 0;
+ element.style.transform = `translate(-50%, -50%) translate(${xOffset / strength}px, ${yOffset / strength}px) rotate(${rotation}deg)`;
+ });
+});
\ No newline at end of file
diff --git a/src/js/blobs.js b/src/js/blobs.js
new file mode 100644
index 0000000..87c71ac
--- /dev/null
+++ b/src/js/blobs.js
@@ -0,0 +1,103 @@
+document.addEventListener('DOMContentLoaded', () => {
+ // Add the grid element to the body
+ const grid = document.createElement('div');
+ grid.classList.add('grid');
+ document.body.prepend(grid); // Add grid to the very back
+
+ const container = document.createElement('div');
+ container.classList.add('blobs'); // Use "blobs" as the container class
+ document.body.prepend(container);
+
+ // Function to get a weighted random value favoring edges
+ const getEdgeBiasedRandom = () => {
+ const rand = Math.random(); // Random number between 0 and 1
+ if (rand < 0.5) {
+ // Favor edges (closer to 10% or 90%)
+ return Math.random() < 0.5 ? getRandomInt(5, 25) : getRandomInt(75, 95);
+ } else {
+ // Occasionally place blobs elsewhere (30-70%)
+ return getRandomInt(25, 75);
+ }
+ };
+
+ // Function to get a random integer between min and max (inclusive)
+ const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
+
+ // Function to adjust the number of blobs based on window size
+ const adjustBlobCount = () => {
+ const width = window.innerWidth;
+ const height = window.innerHeight;
+
+ // Base number of blobs, and scale based on window width and height
+ const baseCount = 5;
+ const widthMultiplier = Math.max(1, Math.floor(width / 500)); // Increase blobs per 500px width
+ const heightMultiplier = Math.max(1, Math.floor(height / 500)); // Increase blobs per 400px height
+
+ const totalCount = Math.min(baseCount * widthMultiplier, baseCount * heightMultiplier);
+
+ // Return number of solids and lines based on adjusted total count
+ const NUM_SOLIDS = Math.max(1, Math.floor(totalCount * 0.3)); // 30% solids
+ const NUM_LINES = totalCount - NUM_SOLIDS; // The rest are lines
+
+ return { NUM_SOLIDS, NUM_LINES };
+ };
+
+ // Get the number of blobs based on the window size
+ const { NUM_SOLIDS, NUM_LINES } = adjustBlobCount();
+
+ // Create and position blobs with distinct z-index layers
+ let zIndexCounter = 1; // Start z-index at 1 to avoid overlap with default content
+
+ // Randomly select and create solids (background blobs)
+ for (let i = 0; i < NUM_SOLIDS; i++) {
+ const x = getRandomInt(1, 13); // Randomly pick a blob design
+ createBlob('background-blob', `blob-freeform-${x}_filled.svg`, zIndexCounter);
+ zIndexCounter += 2; // Increment by 2 to leave gaps for lines
+ }
+
+ // Randomly select and create lines (foreground blobs)
+ for (let i = 0; i < NUM_LINES; i++) {
+ const x = getRandomInt(1, 13); // Randomly pick a blob design
+ const color = Math.random() > 0.5 ? '_red' : ''; // 50% chance to pick the red variant
+ createBlob('background-line', `blob-freeform-${x}${color}.svg`, zIndexCounter - 1); // Ensure lines fit between solids
+ }
+
+ // Function to create and position a blob
+ function createBlob(type, fileName, zIndex) {
+ const blob = document.createElement('img');
+ blob.src = `assets/blobs/${fileName}`; // Corrected path to assets folder
+ blob.classList.add(type);
+ blob.style.top = `${getEdgeBiasedRandom()}%`; // Use edge-biased random for top
+ blob.style.left = `${getEdgeBiasedRandom()}%`; // Use edge-biased random for left
+ blob.style.transform = `translate(-50%, -50%)`;
+ blob.style.position = 'absolute';
+ blob.style.maxWidth = '300%'; // Adjust size
+ blob.style.opacity = type === 'background-line' ? '0.5' : '0.8'; // More transparent for lines
+ blob.style.zIndex = zIndex; // Set z-index dynamically
+ container.appendChild(blob);
+ return blob;
+ }
+
+ // Add mouse movement effect
+ document.addEventListener('mousemove', (event) => {
+ const blobs = document.querySelectorAll('.background-blob');
+ const lines = document.querySelectorAll('.background-line');
+
+ const { clientX: mouseX, clientY: mouseY } = event;
+ const { innerWidth: width, innerHeight: height } = window;
+
+ // Stronger movement scaling factors
+ const xOffset = (mouseX / width - 0.5) * 100; // Amplified to 100 for stronger movements
+ const yOffset = (mouseY / height - 0.5) * 100;
+
+ blobs.forEach((blob, i) => {
+ const strength = 3 + i; // Reduce divisor for stronger movements
+ blob.style.transform = `translate(-50%, -50%) translate(${xOffset / strength}px, ${yOffset / strength}px)`;
+ });
+
+ lines.forEach((line, i) => {
+ const strength = 2 + i * 0.5; // Even stronger for lines
+ line.style.transform = `translate(-50%, -50%) translate(${xOffset / strength}px, ${yOffset / strength}px)`;
+ });
+ });
+});