User:Wyn/Special Issue 26
Study CSS like a linguist
Introduction to the core function of the CSS
In the CSS workshop, We learned the CSS properties like linguists, we explored two fundamental concepts: display and position properties. The display determines how elements interact with each other on a page; The position defines how elements are placed within their container.
We could choose the display and position group, focusing on the fundamental properties that how they work in the webpage.
I learned that display determines how elements relate to each other in the document flow. The key values we covered were:
display: block |
---|
- Elements take up the full width available and start on a new line;
display: inline |
---|
- Elements only take up necessary space and flow within the text
display: inline-block |
---|
- A hybrid that allows width/height settings while flowing inline
display: none |
---|
- blank; Completely removes the element from view
The Position Property:
Position controls where elements are placed relative to other elements or the viewport. We explored:
position: relative |
---|
- Elements can be offset from their normal position
position: absolute |
---|
- Elements are positioned relative to their nearest positioned ancestor
position: fixed |
---|
- Elements are positioned relative to the viewport
CSS drawing
The face metaphor fits the functions of display and position. To understand concepts, I created an interactive face using HTML and CSS. This practice helped me visualize how display and position work together. The face became my canvas (like absolute positioning), with features like ears, eyes, and mouth representing different positioning scenarios. I used slider input (-webkit-slider-thumb) to move the facial features. Also, add transform and translateY to achieve precise positioning of the thumbs. The combination of different display and position values creates complex layouts.
position: relative on the face container creates a positioning context for absolute-positioned elements (like the ears)
position: fixed is similar to the facial feature (like eye-group and eyebrow-group) in the face container and makes different visual effect
display: block is like the face outline - it always takes up the full width available, starting on a new line
display: flex helps arrange eyes and eyebrows in pairs (but I didn't use it in the end) in an organized way
The face metaphor helped me remember that: The face itself (container) is like position: relative, providing a reference point Features like ears (position: absolute) need the face as their positioning context Eyes and eyebrows (display: flex) need to maintain their relationship with neighboring elements.
Learning-by-doing
Aesthetic swap
Dorian introduced a browser extension - scratch that allows direct CSS code modification, changing the webpage styling. This helped us deepen our understanding of CSS by actively transforming existing websites. Rather than just building from zero, I chose to explore how CSS can reshape existing web content into different aesthetic styles.
I am interested in ASCII(American Standard Code for Information Interchange) art recently, as a character encoding standard that became fundamental to early computer and internet communication, it represents a significant moment in internet history and design. In the early days of the internet, when graphical capabilities were limited, based on 128 characters and the monospace font-size, the ASCII art was a creative solution for visual expression. Guidance from Dorian helped me better understand the core concept behind this practice.
Historical context
ASCII art reveals an even deeper historical context. Tracing back to the ages without the internet, typewriter artists were already exploring how standard characters could be arranged to create visual patterns and images, as Joseph explained in the prototype course. Variety typewriters and their historical perspective helped us dig the root of typography and found that ASCII art wasn't just a technical limitation of early computers, but part of a longer tradition of creating art with text characters. From stamp printer to dot matrix-printers, the form of ascii art had transformed serval times.
|
|
Practice! Practice! Practice!
https://pad.xpub.nl/p/csssheet
The key element of ASCII Art is the character itself so that I can play with the font and the font space; It always uses overlap and shadow effects to create a pattern; The frame has character decoration, so I can emphasise to mimic the ASCII Art. For the colour, it is always black and white, but for the nostalgic effect, I add grey and green. The details as follows:
- Character Manipulation:
Monospace fonts ensure each character takes up the same space
Letter spacing and line height control create the text pattern
Using specific ASCII characters like "░▒▓" and "..・。.・゜✧"
- Layering and Shadow Effects:
Pseudo-elements (::before and ::after) create overlapping layers
Text shadows add depth
Mix-blend-mode creates texture through layer interaction
- Decorative Frames:
Button brackets "[ ]"
Border decorations with "-+%$%+-."
Frame-like structures using simple ASCII characters
- Image Treatment:
Converting images to black and white (grayscale)
Increasing contrast for sharper definition
Adding pixelation effects
- Text Effects:
Using opacity for transparency
Creating patterns through character repetition
Implementing hover animations that mimic old computer displays
The main CSS commands:
- Positioning:
position: relative/absolute
top
,left
z-index
- Text Styling:
font-family: monospace
letter-spacing
line-height
text-shadow
- Display and Layout:
display: inline-block/block
overflow: hidden
white-space
word-break
- Visual Effect:
opacity
mix-blend-mode
filter
(for images)content
(for pseudo-elements)
- Border and Decoration:
border
box-shadow
outline
* { font-size: 10px !important; background: initial !important; border-width: 0.1px !important; } img, video { img-rendering: pixelated !important; opacity: 0.8; filter: grayscale(100%) contrast(2); border: 1px dashed green !important; box-shadow: 1px 1px green, -1em 0 .4em olive; overflow: hidden; } a { color: grey !important; text-decoration: underline overline grey !important; font-family: monospace !important; } div { font-family: "Courier New", monospace color: transparent; display: inline-block; letter-spacing: 2em; line-height: 0.5em; text-shadow: 0 0 2px green; position: relative; } div::before { content: "░▒▓"; position: absolute; top: 1px; left: 1px; opacity: 0.8; white-space: pre-wrap; word-break: break-all; } div { border-width: 0.1px !important; } div::after { content: "..・。.・゜✧・.・✫・゜・。"; position: absolute; white-space: pre-wrap; word-break: break-all; top: -1px; left: -1px; opacity: 0.5; z-index: 2 mix-blend-mode: multiply; white-space: pre-wrap; word-break: break-all; text-shadow: 1px 1px 0 green, -1px -1px 0 green, 1px -1px 0 green, -1px 1px 0 green; } textarea, select, div, section, article, header, footer { position: relative; display: block; padding: 10px; margin: 10px 0; color: green !important; outline: none !important; border-left: 0.1px solid green !important; border-right: 0.1px solid green !important; padding-left: 20px; padding-right: 20px; } button, input { font-family: "Courier New", monospace; color: #00ff00; background-color: #000; border: 1px solid #00ff00; padding: 5px 10px; } button::before { content: "[ "; } button::after { content: " ]"; } input::before, section::before, header::before, footer::before { content: "-+*%$%*+-."; position: absolute; top: 0; left: 0; right: 0; text-align: center; z-index: 1; font-weight: bold; } select::before { content:" `~^~^~^~^~^~^`"; font-size: 2 } input::after, section::after, header::after, footer::after { content: "+————————+"; font-size: 2em position: absolute; bottom: 0; left: 0; right: 0; text-align: center; z-index: 0.5em; font-weight: bold; } select::after { content:"`~^~^~^~^~^~^`"; font-size: 2 } span { letter-spacing: -0.5px; line-height: 1.2px; text-shadow: 0.5px 0.5px 1px rgba(0,0,0,0.3); filter: contrast(120%); } .text-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; mix-blend-mode: multiply; } div:hover { border: 1px solid transparent; animation: borderPulse 2s infinite; } @keyframes borderPulse { 0% { border-image: linear-gradient(to right, '#', '.', '*', '#') 1; } 50% { border-image: linear-gradient(to right, '*', '#', '.', '*') 1; } 100% { border-image: linear-gradient(to right, '.', '*', '#', '.') 1; } } div:hover::before { content: "█▓▒░ █▓▒░"; animation: scan 1s linear infinite; } @keyframes scan { 0% { transform: translateY(0); } 100% { transform: translateY(100%); } }
|
Poetics, Concrete Poetry
Based on A Dao of Web Design, I want to use the techniques it mentions, visualizing a concrete poetry:
- colour: less colour more style sheet;
- layout: use(rem, em, %)rather than(px, pt); avoid fixed units (pixels, points);
- font: avoid fixed font sizes (points or pixels); use percentages for font sizing, relative sizes for elements like headings (e.g., 130% of body text);
- content and presentation Separation: avoid presentational HTML tags; use semantic HTML elements (em instead of I) and CSS for controlling page presentation;
According to Zhuangzi's story:
Penumbra asked Shadow:
“Before you moved, now you stop.
Before you sat, now you get up.
Why are you so inconstant?”
Shadow said:
“Shall I wait before doing anything?
Is what I am waiting for also waiting for something?
Shall I wait for the scales of a snake or the wings of a cicada?
How do I know why it is so? How do I know why it isn't so?”
I wanted to visualise all the metaphors in this story, so I tried CSS-drawing but couldn't achieve the effect. I then explored CSS Doodle, but that made my code increasingly complex. Eventually, I decided to use Figma to design the scene and export HTML/CSS for a cleaner implementation.
In this visualization, I've used several elements to represent philosophical concepts:
Shadow and Penumbra: The two overlapping circular forms represent the dialogue participants. I've used 3D transforms to give them an elliptical appearance. Snake sheddings and cicada silhouettes: These symbolize life's transformations - snakes shed their skin to grow, while cicadas emerge transformed after years underground. Stars and sun: These celestial elements represent time's passage and cyclical nature, complementing the conversation about constancy and change.
For the shadow and penumbra effects, I used .shadow-circle, .penumbra-circle {
position: absolute; width: 15rem; height: 15rem; transform: translate(-50%, -50%) rotateX(60deg) scale(1, 0.6);
}
The 3D transformation gives the appearance of an oval seen in perspective, which I found more visually interesting than simple circles.
For the penumbra's textured appearance, I created a repeating linear gradient: .shadow-pattern {
background-image: repeating-linear-gradient(180deg, rgba(50, 100, 150, 0.3) 0px, rgba(50, 100, 150, 0.3) 1px, rgba(70, 120, 170, 0.2) 2px, transparent 3px, transparent 7px);
}
This gives a subtle lined texture without needing external images.
I positioned the dialogue text on opposite sides of the composition to create balance and to visually separate the two speakers. The sun is positioned in the upper right, casting its influence over the entire scene. I made sure to implement responsive breakpoints so the composition works across different screen sizes:
@media (max-width: 1000px) {
.svg-container { width: 50%; } .shadow-circle, .penumbra-circle { width: 160px; height: 160px; }
My sister gave me a suggestion, she thought the snake shed looks like intestine, I'd better represent a good shape of this metaphor. While not perfect, it captures the essence of Zhuangzi's dialogue on action versus inaction, the nature of change, and uncertainty. To respond to a dao of web design, I understand that easy way to create a acessible webpage.
http://hub.xpub.nl/cerealbox/~wyn/pratice/poetry%20css/penumbra_asks_shadow_3.html
From Physiognomy to CSS Grid
My project draws inspiration from a fascinating connection between traditional Chinese physiognomy and CSS grid systems. I was curious about how these seemingly unrelated "positioning systems" could engage in dialogue—physiognomy tells us the "correct position" of facial features, while CSS Grid defines layout rules for web elements. Initially, I experimented with CSS grid functionality to construct a facial interface. I used display: grid and grid-template-columns/rows to create a 3×3 grid as the foundational framework for facial layout:
.face { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: repeat(3, 1fr); width: 80vmin; height: 80vmin; background-color: #ffe6cc; border-radius: 50%; } .left-eye { grid-column: 1; grid-row: 1; } .right-eye { grid-column: 3; grid-row: 1; }
Delving deeper into physiognomy, I discovered the traditional Chinese belief that "a face without moles is precious" (面无善痣为贵)—faces without moles were considered fortunate. This prompted me to contemplate the significance of moles in physiognomy and how to incorporate this concept into CSS property demonstrations.
While researching facial moles and markings, I found that European culture also carried prejudices about moles—specific locations were viewed as a "witch's teat," suggesting the person was possessed by the devil, revealing the dark history of witch hunts. However, I stumbled upon the French "mouches" or artificial beauty marks. These decorative patches weren't merely ornamental but carried social and political significance, representing the pursuit of romance and beauty, and women's expression of their own bodies. These discoveries inspired me to create moles in various shapes:
.mole-star { width: 0; height: 0; border-right: 6px solid transparent; border-bottom: 4.2px solid #333; border-left: 6px solid transparent; transform: rotate(35deg); position: relative; } .mole-plum { width: 10px; height: 10px; position: relative; }
Sliders and Sounds
I initially attempted to use the ::-webkit-slider-thumb feature to allow users to drag and adjust facial feature positions, but found that slider controls didn't visually harmonize with my design. While searching for better interaction methods, I thought of the traditional Chinese sliding puzzle game "Huarong Road" (Klotski). This association inspired me—what if each browser window adjustment could rearrange facial features, like reinterpreting fate from a new perspective? I therefore turned to JavaScript to respond to window resize events:
window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(function() { randomizeFace(); }, 100); }); function randomizeFace() { features.forEach(feature => { const randomColumn = Math.floor(Math.random() * 3) + 1; const randomRow = Math.floor(Math.random() * 3) + 1; feature.style.gridColumn = randomColumn; feature.style.gridRow = randomRow; }); }
Traditional fortune tellers often chant or recite their interpretations, which inspired me to experiment with the Web Audio API to create dynamic sound effects.
whole process...
During the coding process, I first defined the basic HTML structure, creating separate div elements for each facial feature:
div class="face"
<div class="eye left-eye"></div>
<div class="eye right-eye"></div>
<div class="nose"></div>
<div class="mouth"></div>
Then I positioned these elements using CSS:
.face {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: repeat(3, 1fr);
width: 80vmin;
height: 80vmin;
background-color: #ffe6cc;
border-radius: 50%;
}
.left-eye { grid-column: 1; grid-row: 1; }
.right-eye { grid-column: 3; grid-row: 1; }
I also tried with the ::-webkit-slider-thumb CSS feature, hoping to allow users to drag and adjust facial feature positions. This experiment sparked an idea: if facial features could be moved, then their positions would no longer be fixed symbols of destiny but could be changed. However, I found that the slider controls didn't visually align with my design aesthetics.
While researching interaction methods, the traditional Chinese sliding puzzle game made me think about rearranging facial features as a metaphor for reinterpreting destiny from new perspectives. This led me to use JavaScript to respond to browser window resize events:
window.addEventListener('resize', function() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function() {
randomizeFace();
}, 100);
});
This code uses debouncing to avoid frequent triggering and randomly rearranges facial features after each window adjustment. To implement the random rearrangement, I added the randomizeFace() function:
function randomizeFace() {
features.forEach(feature => {
// Randomly generate grid column and row positions from 1-3
const randomColumn = Math.floor(Math.random() * 3) + 1;
const randomRow = Math.floor(Math.random() * 3) + 1;
// Set grid positions
feature.style.gridColumn = randomColumn;
feature.style.gridRow = randomRow;
});
}
Subsequently, I wanted to add sound elements to enrich the interactive experience. I used the Web Audio API to create a dynamic sound system. This was my first attempt at audio programming, which initially presented some javascript challenges:
function playRandomSound() {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
// Random pitch and timbre
const randomFreq = 200 + Math.floor(Math.random() * 400);
oscillator.frequency.value = randomFreq;
oscillator.type = ['sine', 'square', 'triangle', 'sawtooth'][Math.floor(Math.random() * 4)];
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.start();
setTimeout(() => oscillator.stop(), 500);
}
While implementing sounds, I encountered several issues, such as browsers requiring user interaction before playing sounds and the timing of audio context creation. For the mole designs, I initially used simple dots, but during my research, I stumbled upon the historical French "mouches" (beauty marks) culture. This inspired me to create more diverse shapes for the moles. I used CSS pseudo-elements and border tricks to create star-shaped moles:
.mole-star {
width: 0;
height: 0;
border-right: 6px solid transparent;
border-bottom: 4.2px solid #333;
border-left: 6px solid transparent;
transform: rotate(35deg);
position: relative;
}
.mole-star:before, .mole-star:after {
content: '';
position: absolute;
}
The plum blossom-shaped moles were created by combining multiple small dots:
.mole-plum {
width: 10px;
height: 10px;
position: relative;
}
.mole-plum:before, .mole-plum:after {
content: '';
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
}
When dynamically creating these elements with JavaScript, I learned how to manipulate the DOM to replace content and apply different style classes:
const shapes = ['circle', 'star', 'plum'];
const randomShape = shapes[Math.floor(Math.random() * shapes.length)];
// Clear existing content
mole.innerHTML = '';
// Create elements based on random shape
if (randomShape === 'circle') {
const circle = document.createElement('div');
circle.className = 'mole-circle';
mole.appendChild(circle);
}
Throughout the process, the biggest technical challenge was coordinating visual changes with sound generation. I researched how to associate sounds with specific facial layouts, allowing users to experience the concept of destiny remapping through both visual and auditory senses.
http://hub.xpub.nl/cerealbox/~wyn/pratice/soundboard%20practice http://hub.xpub.nl/cerealbox/~wyn/pratice/facenotation/facenotation.html
The third try:
https://hub.xpub.nl/cerealbox/~wyn/pratice/face-resize/faceresize.html