Main Content
Your page content goes here...
A 5-day guide from messy beginner to confident web maker. Click each day tab to progress at your own pace.
yourname.neocities.org)index.html to editEvery website is just files. Here's what you start with:
In your index.html, find the text between <h1> and </h1> tags. Change it to your name:
Try typing in the box below:
<!DOCTYPE html>
<html>
<head>
<title>My Site</title> <!-- Shows in browser tab -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Big Heading</h1>
<p>A paragraph of text.</p>
</body>
</html>
What things are
(headings, paragraphs, images)
How things look
(colors, sizes, spacing)
| Tag | What It Does |
|---|---|
<h1> to <h6> | Headings (h1 biggest, h6 smallest) |
<p> | Paragraph of text |
<a href="..."> | Link to another page |
<img src="..."> | Display an image |
<div> | Generic container/box |
<span> | Inline container for text |
<ul>, <ol>, <li> | Lists (unordered, ordered, item) |
selector {
property: value;
}
h1 { โ selector (what to style)
color: red; โ property: value;
font-size: 2em;
}
Link your CSS file in the <head> of every HTML page:
<head>
<title>My Site</title>
<link rel="stylesheet" href="style.css">
</head>
Then in style.css:
body {
font-family: Helvetica, Arial, sans-serif;
margin: 0;
padding: 2em;
background: #fff;
color: #111;
}
h1 {
font-size: 2em;
margin-bottom: 0.5em;
}
a {
color: #111;
}
a:hover {
color: #666;
}
images folder in Neocities<!-- Basic image -->
<img src="images/photo.jpg" alt="Description of the image">
Use <figure> and <figcaption> to add a caption to your image:
<figure>
<img src="images/photo.jpg" alt="Description of image">
<figcaption>Photo caption goes here</figcaption>
</figure>
<img> alone
The alt="" attribute describes the image for screen readers and shows if the image fails to load. Always include it!
In your Neocities dashboard:
about.htmlimages/ folder.
A well-structured page has semantic sections:
<body>
<header>
<nav>
<a href="index.html">Home</a>
<a href="about.html">About</a>
<a href="work.html">Work</a>
</nav>
</header>
<main>
<h1>Page Title</h1>
<p>Your content here...</p>
</main>
<footer>
<p>© 2026 Your Name</p>
</footer>
</body>
Your page content goes here...
nav {
display: flex;
gap: 1.5em;
padding: 1em;
background: #111;
}
nav a {
color: #fff;
text-decoration: none;
}
nav a:hover {
color: #a6e;
}
The easiest way to add fonts is with Google Fonts:
<link> code<head> before your CSS link<head>
<!-- Google Font -->
<link href="https://fonts.googleapis.com/css2?family=Space+Mono&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
Then use it in your CSS:
body {
font-family: 'Space Mono', monospace;
}
h1 {
font-family: 'Playfair Display', serif;
}
Serif โ has little feet (Times, Georgia)
Sans-serif โ clean lines (Helvetica, Arial)
Monospace โ equal width (Courier, Monaco)
h1 {
font-size: 2.5em; /* Size (em = relative to parent) */
font-weight: 700; /* Boldness: 100-900 */
line-height: 1.2; /* Space between lines */
letter-spacing: 0.05em; /* Space between letters */
text-transform: uppercase; /* CAPS, lowercase, Capitalize */
}
line-height: 1.5 for readable body textem or rem for sizes (scales better)CSS supports several ways to write colors:
#a6e or #aa66eergb(170, 102, 238)rgba(170, 102, 238, 0.5)hsl(270, 80%, 67%)/* Text shadow: x-offset y-offset blur color */
h1 {
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
/* Box shadow */
.card {
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
/* Gradients */
background: linear-gradient(to right, #a6e, #58f);
background: radial-gradient(circle, #fff, #a6e);
Flexbox is perfect for one-dimensional layouts (rows or columns):
.container {
display: flex;
gap: 1em; /* Space between items */
justify-content: center; /* Horizontal alignment */
align-items: center; /* Vertical alignment */
flex-wrap: wrap; /* Wrap to new line */
}
Grid is perfect for two-dimensional layouts (rows AND columns):
.gallery {
display: grid;
grid-template-columns: repeat(3, 1fr); /* 3 equal columns */
gap: 1em;
}
/* Responsive: 2 columns on tablet, 1 on mobile */
@media (max-width: 800px) {
.gallery {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 500px) {
.gallery {
grid-template-columns: 1fr;
}
}
Use @media queries to change layouts at different screen sizes. Common breakpoints:
max-width: 500px โ Mobile phonesmax-width: 800px โ Tabletsmax-width: 1200px โ Small laptops<!-- Same folder -->
<a href="about.html">About</a>
<!-- Inside a folder -->
<a href="pages/about.html">About</a>
<!-- Up one folder -->
<a href="../index.html">Home</a>
<!-- External link (opens in new tab) -->
<a href="https://example.com" target="_blank">External</a>
<!-- Email link -->
<a href="mailto:you@email.com">Email Me</a>
Links have different states you can style:
a {
color: #111;
text-decoration: none;
}
a:hover { /* Mouse over */
color: #a6e;
}
a:active { /* Being clicked */
color: #58f;
}
a:visited { /* Already visited */
color: #666;
}
Make links and buttons look clickable:
.button {
display: inline-block;
padding: 0.8em 1.5em;
background: #111;
color: #fff;
text-decoration: none;
border: none;
cursor: pointer;
font-family: inherit;
font-size: 1em;
transition: all 0.2s ease;
}
.button:hover {
background: #333;
transform: translateY(-2px);
}
/* Outline style */
.button-outline {
background: transparent;
border: 2px solid #111;
color: #111;
}
.button-outline:hover {
background: #111;
color: #fff;
}
In HTML, use it like this:
<a href="contact.html" class="button">Contact Me</a>
<button class="button">Submit</button>
Make elements respond when users hover:
/* Scale up on hover */
.card:hover {
transform: scale(1.05);
}
/* Always add transition for smooth effects */
.card {
transition: transform 0.2s ease;
}
/* Change multiple properties */
a {
color: #111;
transition: color 0.2s, transform 0.2s;
}
a:hover {
color: #a6e;
transform: translateX(5px);
}
Without transition, changes are instant and jarring. Always add transition: all 0.2s ease; for smooth effects.
Change the mouse cursor for interactive elements:
/* Common cursor values */
.clickable { cursor: pointer; } /* Hand */
.draggable { cursor: grab; } /* Grab hand */
.loading { cursor: wait; } /* Spinner */
.disabled { cursor: not-allowed; } /* No symbol */
/* Custom cursor image */
body {
cursor: url('my-cursor.png'), auto;
}
Use emoji or icon libraries to add visual elements:
Just type or paste emoji directly in your HTML!
<a href="mailto:me@email.com">๐ง Email me</a>
<span>Made with โค๏ธ</span>
Add this to your <head>:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css">
Then use icons:
<i class="fa-brands fa-instagram"></i> Instagram
<i class="fa-solid fa-envelope"></i> Email
The favicon is the small icon in the browser tab:
favicon.ico or favicon.png<head>:<link rel="icon" type="image/png" href="favicon.png">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>๐</text></svg>">
Create collapsible sections with just HTML & CSS:
<details>
<summary>Click to expand</summary>
<p>Hidden content goes here...</p>
</details>
This content was hidden until you clicked! No JavaScript needed.
Now that you know how to insert images (Day 1), CSS lets you style and treat them in creative ways:
img {
max-width: 100%; /* Responsive: never wider than container */
height: auto; /* Maintain aspect ratio */
}
/* Rounded corners */
.rounded { border-radius: 8px; }
.circle { border-radius: 50%; }
/* Borders & shadows */
.framed {
border: 3px solid #111;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
/* Filters */
.grayscale { filter: grayscale(100%); }
.blur { filter: blur(2px); }
.bright { filter: brightness(1.2); }
.sepia { filter: sepia(100%); }
/* Hover effect: color on hover */
.hover-color {
filter: grayscale(100%);
transition: filter 0.3s ease;
}
.hover-color:hover {
filter: grayscale(0%);
}
Control how images fill their containers:
.image-container {
width: 200px;
height: 200px;
overflow: hidden;
}
.image-container img {
width: 100%;
height: 100%;
object-fit: cover; /* Crop to fill (most common) */
/* object-fit: contain; โ Fit inside, may have gaps */
/* object-fit: fill; โ Stretch to fill (may distort) */
object-position: center; /* Where to anchor the crop */
}
alt="" text for accessibilitymax-width: 100% for responsive imagesobject-fit: cover for consistent grid layoutsTomorrow we'll add JavaScript to make your site truly interactive. Here's a preview:
What's on the page
How it looks
What it does (interactivity, animations, logic)
JavaScript lets you:
What's on the page
How it looks
What it does (interactivity, animations, logic)
Add a <script> tag at the end of your <body>:
<body>
<!-- Your HTML content -->
<script>
// Your JavaScript code here
console.log('Hello from JavaScript!');
</script>
</body>
JavaScript runs immediately when loaded. Put it at the bottom so all your HTML elements exist first!
// Select one element by ID
const heading = document.getElementById('main-title');
// Select one element by CSS selector
const button = document.querySelector('.button');
// Select ALL matching elements
const cards = document.querySelectorAll('.card');
<button id="myBtn">Click Me</button>
<script>
document.getElementById('myBtn').addEventListener('click', function() {
alert('Button clicked!');
});
</script>
<button id="toggleBtn">Toggle Dark Mode</button>
<script>
document.getElementById('toggleBtn').addEventListener('click', function() {
document.body.classList.toggle('dark-mode');
});
</script>
Then in your CSS:
body.dark-mode {
background: #111;
color: #fff;
}
body.dark-mode a {
color: #a6e;
}
<div class="draggable" style="position: absolute; left: 100px; top: 100px;">
Drag me!
</div>
<script>
document.querySelectorAll('.draggable').forEach(el => {
let isDragging = false;
let offsetX, offsetY;
el.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - el.offsetLeft;
offsetY = e.clientY - el.offsetTop;
el.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
el.style.left = (e.clientX - offsetX) + 'px';
el.style.top = (e.clientY - offsetY) + 'px';
});
document.addEventListener('mouseup', () => {
isDragging = false;
el.style.cursor = 'grab';
});
});
</script>
The code above works for mouse. For touch devices, you need touch events:
el.addEventListener('touchstart', (e) => { ... });
el.addEventListener('touchmove', (e) => { ... });
el.addEventListener('touchend', () => { ... });
// Change text
document.getElementById('title').textContent = 'New Title';
// Change HTML
document.getElementById('container').innerHTML = '<p>New content</p>';
// Change styles
document.getElementById('box').style.background = 'red';
document.getElementById('box').style.display = 'none';
el.classList.toggle('active')mouseenter and mouseleavemousemoveMath.random().toString(16).slice(2,8)setTimeoutFor larger projects, put JavaScript in a separate file:
<!-- In your HTML -->
<script src="script.js"></script>
<!-- Then in script.js -->
document.addEventListener('DOMContentLoaded', function() {
// Your code here - runs after page loads
});
my-neocities-site
index.html โ "Open with Live Server"
When you're ready to publish your changes:
images/photo.jpg locally, make sure the folder structure is the same on Neocities.
You now have all the skills you need. These days are for building, experimenting, and polishing your site.
../
When your HTML is inside a folder (like projects/project1.html), you need to "walk back up" to find your CSS and images:
<!-- From projects/project1.html -->
<link rel="stylesheet" href="../css/style.css">
<img src="../images/hero.jpg">
<a href="../index.html">Back to Home</a>
../ means "go up one folder"../../ means "go up two folders"projects/project1.html, use ../css/style.css to reach the css folderUse a projects/ folder when you have:
Before making big changes:
index-jan19.html_backups folder_backups makes it sort to the top of your folder. You can also add it to .gitignore if you use Git later.
| Problem | Solution |
|---|---|
| CSS not loading | Check your <link> path. Try hard refresh: Cmd+Shift+R |
| Image not showing | Check file path, extension, and that there are no spaces in filename |
| Link not working | Check the file exists and the href path is correct |
| Works locally, not on Neocities | Make sure you uploaded ALL files including folders |
Every page should have the same navigation:
<nav>
<a href="index.html">Home</a>
<a href="about.html">About</a>
<a href="work.html">Work</a>
<a href="blog.html">Blog</a>
<a href="contact.html">Contact</a>
</nav>
https://YOURNAME.neocities.org
Handy shortcuts and quick fixes you'll use all the time.
| Action | Mac | Windows |
|---|---|---|
| Save | Cmd+S | Ctrl+S |
| Undo | Cmd+Z | Ctrl+Z |
| Redo | Cmd+Shift+Z | Ctrl+Y |
| Select All | Cmd+A | Ctrl+A |
| Copy | Cmd+C | Ctrl+C |
| Paste | Cmd+V | Ctrl+V |
| Hard Refresh (clear cache) | Cmd+Shift+R | Ctrl+Shift+R |
| Open DevTools | Cmd+Option+I | F12 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Page Title</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- Your content here -->
</body>
</html>
<!-- Same folder -->
<a href="about.html">About</a>
<!-- Inside a folder -->
<a href="pages/about.html">About</a>
<!-- Up one folder -->
<a href="../index.html">Home</a>
<!-- External link (opens in new tab) -->
<a href="https://example.com" target="_blank">External</a>
<!-- Basic image -->
<img src="images/photo.jpg" alt="Description">
<!-- With figure and caption -->
<figure>
<img src="images/photo.jpg" alt="Description">
<figcaption>Caption text</figcaption>
</figure>
<link> is in <head>src=""/* Text */
color: #111;
font-size: 16px;
font-family: Helvetica, sans-serif;
text-align: center;
/* Spacing */
margin: 20px; /* Outside */
padding: 20px; /* Inside */
/* Box */
background: #f0f0f0;
border: 1px solid #ccc;
border-radius: 4px;
/* Layout */
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
Upload directly from VS Code without visiting the website:
Access your Neocities files like a folder on your computer:
https://neocities.org/webdav/https://neocities.org/webdav/For power users: upload with a single command:
# Install the Neocities CLI (requires Ruby)
$ gem install neocities
# Push your entire folder to Neocities
$ neocities push .
# First time will ask for credentials
neocities push . from your project folder.
Automate uploads with the Neocities API:
# Get your API key
$ curl -u "YOUR_USERNAME:YOUR_PASSWORD" "https://neocities.org/api/key"
# Upload a file
$ curl -u "USER:PASS" -F "index.html=@index.html" https://neocities.org/api/upload
The workflow you've learned works everywhere:
| Host | Best For | How to Deploy |
|---|---|---|
| GitHub Pages | Free, professional | Push to a Git repository |
| Netlify | Free, auto-deploys | Drag and drop or connect GitHub |
| Vercel | Free, great for frameworks | Connect to GitHub |
| Glitch | Collaborative, beginner-friendly | Edit in browser |
| Surge.sh | Super simple CLI | surge command |
Professional version control (never lose work again):
# Initialize a repository
$ git init
# Stage all files
$ git add .
# Commit with a message
$ git commit -m "Initial site"
# Connect to GitHub and push
$ git remote add origin https://github.com/you/repo.git
$ git push -u origin main