Build a Local Business Website
Build, test, and publish a responsive one-page website for a local business.
Introduction
30 Second Summary
Local customers often check a business on their phone before making contact. A confusing page can send that customer elsewhere in seconds.
In this project, you will build a one-page website for the fictional Northstar Home Services business using responsive design. You will publish the finished demo with GitHub Pages before packaging the work as a $99 introductory offer in Google Docs.
What You'll Build
Your finished demo gives a potential client a polished service page that stays readable from a wide desktop down to a narrow phone.
By the end of this project, you'll have:
- A clear service journey where visitors can understand the offer before jumping to the Services, Process, or Contact section.
- A responsive layout that you have tested by exposing a desktop-only failure in Chrome DevTools before repairing it for a narrow screen.
- A client-ready sales kit containing a public demo link, a $99 fixed-price offer, and a direct outreach message.
- Secret Mission: Turn a Lighthouse report into a three-finding website audit with a $25 Website Checkup upsell.
Are there any prerequisites?
You do not need coding or freelance experience. You only need a Mac with internet access plus a Google account.
Before We Start
This step turns a practice project into a commitment to build something a real local business could use. Choosing your first audience gives every design decision a clear purpose.
Set Up Your Free Web Workshop
A potential client can only evaluate work that you can create reliably. You need dependable tools before the website itself can take shape.
Visual Studio Code gives your project files one place to live. Google Chrome shows the page that visitors experience.
GitHub later gives the finished website a public home. A verified account prepares you for that publishing step.
In this step, get ready to:
- Install Google Chrome and Visual Studio Code on your Mac.
- Create a verified GitHub account.
- Create a local folder with a visible setup page.
Install your browser and code editor
Mac apps often arrive inside disk image files. Moving each app into Applications keeps it available after you close the installer.
- Download Google Chrome for macOS from the official installation page.
- Open googlechrome.dmg from your Downloads folder.
You will see the Google Chrome installer window.
- Drag Chrome into the Applications folder.
Google Chrome now appears in Applications.
- Download Visual Studio Code for macOS from the official setup page.
- Open the Visual Studio Code .dmg file from your Downloads folder.
You will see the Visual Studio Code installer window.
- Drag Visual Studio Code.app into the Applications folder.
Visual Studio Code now appears beside your other installed apps.
- Open the Applications folder in Finder.
- Double-click Google Chrome to open it.
- Return to the Applications folder.
- Double-click Visual Studio Code to open it.
Good start. Both workshop apps now open on your Mac.
App Missing From Applications?
- Reopen the app's disk image from your Downloads folder.
- Drag the app into Applications again.
- Wait for macOS to finish copying the app before opening it.
Need another pair of eyes? Help me finish installing Google Chrome or Visual Studio Code on my Mac. You can also share the same details in the NextWork community.
Create and verify your GitHub account
GitHub requires a personal account with a verified email before you publish the website later. Your Google account provides a direct signup path.
- Open the GitHub signup page in Google Chrome.
- Select Continue with Google.
You will see a Google account selection screen.
- Select the Google account you want to connect.
- Complete the requested GitHub account details.
GitHub sends a verification message to the email address connected to your account.
- Open the verification email from GitHub.
- Use the verification link to confirm your email address.
- Return to GitHub in Google Chrome.
That account is ready for publishing. You now have the verified identity that your public demo needs later.
Verification Email Missing?
- Check the inbox for the Google account you selected.
- Check the spam folder connected to that account.
- Return to the GitHub signup flow to request another verification email.
Still waiting for the message? Help me verify my new GitHub account. You can also share the same details in the NextWork community.
Create your first local page
An HTML file controls what a browser displays. A CSS file controls how that content looks.
Visual Studio Code may show Workspace Trust after you open the folder. You created this folder yourself, so you can trust its contents.
- Switch back to Visual Studio Code.
- Select File in the macOS menu bar.
- Select Open Folder....
- Choose Desktop in the folder picker.
- Select New Folder.
- Enter local-business-demo as the folder name.
The folder picker now lists your new local-business-demo folder on the Desktop.
- Select the local-business-demo folder.
- Click Open.
- Select Yes, I trust the authors if Workspace Trust appears.
You will see local-business-demo at the top of the Explorer view. This confirms that Visual Studio Code opened the correct folder.
- Select New File... in the Explorer view.
- Enter index.html as the file name.
- Press Enter to create the file.
You will see index.html beneath the folder name in the Explorer view.
- Select New File... again.
- Enter styles.css as the file name.
- Press Enter to create the file.
You will see both project files in the Explorer view. The new styles.css file is empty.
Why Start With Plain HTML and CSS?
Plain HTML and CSS run directly in a browser. This keeps the project free from frameworks.
You also avoid package installation and build steps. Your attention stays on the website a client sees.
- Select index.html in the Explorer view.
- Add the setup message by replacing the file contents with the code below:
Setup complete
What Does This File Do?
- The setup message gives the browser one visible line to display.
- That line proves Google Chrome is reading the correct local file.
- Save index.html by pressing Cmd+S.
- Leave styles.css empty.
- Predict which line Google Chrome will display before you open the page.
- Control-click index.html in the Explorer view.
- Select Reveal in Finder.
- Switch back to Google Chrome.
- Drag index.html from Finder into the Google Chrome window.
You have your first visible result. Google Chrome displays Setup complete from the file you wrote in Visual Studio Code.
Setup Message Not Showing?
- Check that the file name is exactly index.html.
- Save index.html again.
- Drag index.html into Google Chrome instead of styles.css.
Still seeing the wrong page? Help me open my local index.html file in Google Chrome.
✔️ Awesome, I've got everything!
Your local-business-demo folder contains both files. Google Chrome displays the setup message from index.html.
ⓧ I'd like to double check the full code
Your complete index.html file contains this single line:
Setup complete
Why Is This File So Short?
This temporary message tests the connection between your file and the browser. The complete website replaces it in the next step.
Your complete styles.css file contains no text. Keep the file completely empty.
Your free web workshop is ready. Next up, you will replace the setup message with the complete Northstar Home Services page.
Build the First Working Page
Your free workshop is ready in Visual Studio Code. Google Chrome proves that your local index.html file can load.
A complete page gives you something real to test before visual styling adds complexity. You will build that first working product with semantic HTML.
In this step, get ready to:
- Create the complete Northstar Home Services page with semantic HTML.
- Keep styles.css linked as an empty stylesheet.
- Verify that each navigation link jumps to its matching section.
Build the semantic page structure
Semantic HTML gives each page section a clear purpose. The browser can display this structure before you add any visual styling.
- Select index.html in the Explorer sidebar.
- Highlight the existing Setup complete text in the editor.
- Replace the selected text with the document foundation by pasting the code below:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Northstar Home Services</title>
<meta
name="description"
content="A portfolio demo for a clear, responsive local-business website."
>
<link rel="stylesheet" href="styles.css">
</head>
<body id="top">
<main>
</main>
</body>
</html>
What Does This Foundation Do?
- The head stores the page title plus information for browsers.
- The stylesheet link connects the empty styles.css file to the page.
- The main element reserves the space for the business content.
- Save index.html.
- Return to Google Chrome.
- Refresh the page.
The page content area is blank. The browser tab now reads Northstar Home Services.
Still Seeing the Setup Text?
Confirm that you replaced the contents of index.html inside the local-business-demo folder. Help me find why Google Chrome still displays the old setup page.
- Find the opening <main> line in index.html.
- Place your cursor on the line directly above <main>.
- Add the page header by pasting the code below:
<header class="site-header">
<div class="site-shell nav-wrap">
<a class="brand" href="#top">Northstar Home Services</a>
<nav aria-label="Primary navigation">
<ul class="nav-links">
<li><a href="#services">Services</a></li>
<li><a href="#process">Process</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</div>
</header>
What Does the Header Do?
- The brand link points to #top so visitors can return to the start of the page.
- The navigation contains one link for each main business section.
- Each link uses a page fragment that matches a section ID added later.
- Save index.html.
- Return to Google Chrome.
- Refresh the page.
You should see Northstar Home Services at the top. You should also see the Services, Process, and Contact links.
Header Missing?
Check that the header sits inside <body id="top"> and above <main>. Help me fix a missing HTML header in my Northstar page.
- Find the blank space between <main> and </main>.
- Add the hero section by pasting the code below:
<section class="hero">
<div class="site-shell hero-content">
<p class="eyebrow">Friendly help for busy homeowners</p>
<h1>Reliable help for every corner of your home.</h1>
<p class="hero-copy">
Northstar Home Services handles everyday repairs, seasonal upkeep,
and small improvement projects with clear communication from start
to finish.
</p>
<div class="button-row">
<a class="button button-primary" href="#services">View services</a>
<a class="button button-secondary" href="#contact">Request a quote</a>
</div>
</div>
</section>
What Does the Hero Add?
- The hero introduces the business promise at the start of the page.
- The supporting copy explains the kind of help Northstar provides.
- The two links guide visitors toward the services section or the contact section.
- Save index.html.
- Return to Google Chrome.
- Refresh the page.
You should see the main Northstar promise below the navigation. Two plain links should appear underneath the introductory copy.
Hero Copy Missing?
Confirm that the hero sits between the opening and closing main tags. Help me repair the hero section in my HTML page.
- Find the closing </section> tag for the hero.
- Place your cursor on the next line.
- Add the services section by pasting the code below:
<section class="section" id="services" aria-labelledby="services-title">
<div class="site-shell">
<p class="eyebrow">Services</p>
<h2 id="services-title">Practical support without the runaround</h2>
<div class="card-grid">
<article class="card">
<h3>Small repairs</h3>
<p>Help with shelves, doors, fixtures, and the jobs that keep getting postponed.</p>
</article>
<article class="card">
<h3>Seasonal upkeep</h3>
<p>Simple maintenance visits that help prepare a home for the months ahead.</p>
</article>
<article class="card">
<h3>Room refreshes</h3>
<p>Focused improvements that make an everyday space feel cleaner and more useful.</p>
</article>
</div>
</div>
</section>
How Are the Services Organized?
- The section ID gives the Services navigation link a destination.
- Each article represents one service a customer can understand on its own.
- The shared card class prepares all three services for consistent styling.
- Save index.html.
- Return to Google Chrome.
- Refresh the page.
You should see Small repairs first. Seasonal upkeep and Room refreshes should follow it.
Missing a Service?
Check that the services section contains three complete article elements. Help me find a missing service card in my HTML.
- Find the section containing id="services".
- Place your cursor after that section's closing </section> tag.
- Add the process section by pasting the code below:
<section class="section section-muted" id="process" aria-labelledby="process-title">
<div class="site-shell">
<p class="eyebrow">Process</p>
<h2 id="process-title">A simple path from question to completion</h2>
<ol class="process-grid">
<li>
<span>1</span>
<h3>Share the job</h3>
<p>Describe the task, timing, and any photos that make the request clearer.</p>
</li>
<li>
<span>2</span>
<h3>Confirm the plan</h3>
<p>Agree on the scope, schedule, and price before the work begins.</p>
</li>
<li>
<span>3</span>
<h3>Get it finished</h3>
<p>Review the completed work and ask any final questions before sign-off.</p>
</li>
</ol>
</div>
</section>
Why Use an Ordered List?
- The ordered list tells the browser that the process follows a sequence.
- Each list item holds one stage of the customer journey.
- The numbered spans provide visible step markers for later styling.
- Save index.html.
- Return to Google Chrome.
- Refresh the page.
You should see a three-step process below the services. The sequence starts with Share the job and finishes with Get it finished.
Process Steps Out of Order?
Confirm that all three steps sit inside the same ol element. Help me repair the ordered process list in my HTML.
- Find the section containing id="process".
- Place your cursor after that section's closing </section> tag.
- Add the contact section by pasting the code below:
<section class="section" id="contact" aria-labelledby="contact-title">
<div class="site-shell contact-panel">
<div>
<p class="eyebrow">Contact</p>
<h2 id="contact-title">Ready to discuss the next small project?</h2>
<p>Replace the sample email with the address you want potential clients to use.</p>
</div>
<a class="button button-primary" href="mailto:your-email@example.com">Request a quote</a>
</div>
</section>
What Does the Contact Section Do?
- The contact section gives the Contact navigation link a destination.
- The heading asks the visitor to take the next step.
- The email link prepares a direct contact action for the finished demo.
- Save index.html.
- Return to Google Chrome.
- Refresh the page.
You should see the contact question below the process. A Request a quote link should appear with it.
Contact Section Missing?
Check that the contact section remains inside main and follows the process section. Help me fix the contact section in my Northstar page.
- Find the closing </main> tag.
- Place your cursor on the line directly below </main>.
- Finish the page with the footer by pasting the code below:
<footer>
<div class="site-shell">
<p>Portfolio demo created to showcase a one-page website service.</p>
</div>
</footer>
Why Include the Footer?
The footer identifies the page as a portfolio demo. This keeps the fictional Northstar example clear to anyone who opens it.
- Save index.html.
- Return to Google Chrome.
- Refresh the page.
You should see the portfolio-demo statement at the bottom of the page. Your complete unstyled page is now visible from top to bottom.
Footer Appearing Inside Contact?
Confirm that the footer sits after </main> and before </body>. Help me move the footer to the correct place in my HTML document.
✔️ Awesome, I've got everything!
Great. Double check that you saved index.html and can see the complete unstyled page in Google Chrome.
ⓧ I'd like to double check the full code
Compare your complete index.html file with this version:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Northstar Home Services</title>
<meta
name="description"
content="A portfolio demo for a clear, responsive local-business website."
>
<link rel="stylesheet" href="styles.css">
</head>
<body id="top">
<header class="site-header">
<div class="site-shell nav-wrap">
<a class="brand" href="#top">Northstar Home Services</a>
<nav aria-label="Primary navigation">
<ul class="nav-links">
<li><a href="#services">Services</a></li>
<li><a href="#process">Process</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</div>
</header>
<main>
<section class="hero">
<div class="site-shell hero-content">
<p class="eyebrow">Friendly help for busy homeowners</p>
<h1>Reliable help for every corner of your home.</h1>
<p class="hero-copy">
Northstar Home Services handles everyday repairs, seasonal upkeep,
and small improvement projects with clear communication from start
to finish.
</p>
<div class="button-row">
<a class="button button-primary" href="#services">View services</a>
<a class="button button-secondary" href="#contact">Request a quote</a>
</div>
</div>
</section>
<section class="section" id="services" aria-labelledby="services-title">
<div class="site-shell">
<p class="eyebrow">Services</p>
<h2 id="services-title">Practical support without the runaround</h2>
<div class="card-grid">
<article class="card">
<h3>Small repairs</h3>
<p>Help with shelves, doors, fixtures, and the jobs that keep getting postponed.</p>
</article>
<article class="card">
<h3>Seasonal upkeep</h3>
<p>Simple maintenance visits that help prepare a home for the months ahead.</p>
</article>
<article class="card">
<h3>Room refreshes</h3>
<p>Focused improvements that make an everyday space feel cleaner and more useful.</p>
</article>
</div>
</div>
</section>
<section class="section section-muted" id="process" aria-labelledby="process-title">
<div class="site-shell">
<p class="eyebrow">Process</p>
<h2 id="process-title">A simple path from question to completion</h2>
<ol class="process-grid">
<li>
<span>1</span>
<h3>Share the job</h3>
<p>Describe the task, timing, and any photos that make the request clearer.</p>
</li>
<li>
<span>2</span>
<h3>Confirm the plan</h3>
<p>Agree on the scope, schedule, and price before the work begins.</p>
</li>
<li>
<span>3</span>
<h3>Get it finished</h3>
<p>Review the completed work and ask any final questions before sign-off.</p>
</li>
</ol>
</div>
</section>
<section class="section" id="contact" aria-labelledby="contact-title">
<div class="site-shell contact-panel">
<div>
<p class="eyebrow">Contact</p>
<h2 id="contact-title">Ready to discuss the next small project?</h2>
<p>Replace the sample email with the address you want potential clients to use.</p>
</div>
<a class="button button-primary" href="mailto:your-email@example.com">Request a quote</a>
</div>
</section>
</main>
<footer>
<div class="site-shell">
<p>Portfolio demo created to showcase a one-page website service.</p>
</div>
</footer>
</body>
</html>
Your linked styles.css file should still be empty.
Test the unstyled navigation
Each navigation link uses a fragment such as #services. The browser matches that fragment to the section with the same ID.
- Select styles.css in the Explorer sidebar.
The editor should contain no text. This confirms that the browser is displaying the HTML structure without project styling.
- Return to index.html in the Explorer sidebar.
- Save index.html.
- Return to Google Chrome.
Before you test the page, where do you expect each navigation link to move you?
- Refresh the page.
You should see the Northstar Home Services header and hero. The three services should appear farther down the page.
The three-step process should follow the services. The contact section and portfolio footer should complete the page.
- Click Services in the page header.
The browser should jump to Practical support without the runaround.
- Click Process in the page header.
The browser should jump to A simple path from question to completion.
- Click Contact in the page header.
The browser should jump to Ready to discuss the next small project.
Navigation Link Not Jumping?
- Check that the Services link uses href="#services".
- Check that the matching section uses id="services".
- Repeat the same comparison for process and contact.
Still stuck? Help me find why an anchor link does not jump to its matching section.
That first product slice is working. Your complete page loads locally and its navigation connects visitors to the right content.
Your business demo now has all of its content in place. Next up, you will turn the plain page into a polished desktop design and uncover its mobile weakness.
Style the Desktop Demo
Your Northstar Home Services page already contains every section a potential client needs. The linked styles.css file gives you a place to turn that structure into a polished demo.
A polished desktop layout creates a credible first impression. A narrow viewport tests whether that impression survives on a smaller screen.
In this step, get ready to:
- Add the colors, typography, spacing, buttons, cards, contact panel, and three-column grids.
- Build the desktop layout around a fixed 960px content shell.
- Test the styled page with the Mobile M viewport in Chrome DevTools.
Create the visual foundation
Your page needs a consistent visual system before individual sections can feel connected. CSS custom properties keep the main colors available from one shared location.
- In the styles.css tab from earlier, add the page-wide colors and base styles by pasting this code:
:root {
--ink: #172033;
--muted: #5b6474;
--surface: #ffffff;
--surface-soft: #eef4f3;
--brand: #12645a;
--brand-dark: #0b4942;
--accent: #f2b84b;
--line: #dce5e3;
--shadow: 0 18px 45px rgba(23, 32, 51, 0.1);
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
color: var(--ink);
background: var(--surface);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.6;
}
What do these base styles do?
- The custom properties store the colors and shadow used throughout the page.
- The universal selector makes element dimensions include their padding and borders.
- The html rule makes navigation jumps scroll smoothly.
- The body rule removes the browser margin.
- The body styles also establish the page colors, font, and line spacing.
- Save styles.css.
- Return to the Northstar Home Services page in Google Chrome.
- Refresh the page.
You'll see the default page margin disappear. The text now uses the new dark color and system font stack.
Still seeing the original browser styles?
Confirm that you saved styles.css inside the local-business-demo folder. Check that index.html still links to styles.css.
Still stuck? Help me find why my linked CSS file is not changing my local HTML page.
The visual foundation now controls the page itself. The next rules create a centered content shell with a structured header.
- Append the shared shell and header rules to styles.css by pasting this code below the body rule:
a {
color: inherit;
}
.site-shell {
width: 960px;
margin-inline: auto;
}
.site-header {
border-bottom: 1px solid var(--line);
background: rgba(255, 255, 255, 0.96);
}
.nav-wrap {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 4.5rem;
gap: 1.5rem;
}
.brand {
color: var(--brand-dark);
font-weight: 800;
text-decoration: none;
}
How does the header take shape?
- The .site-shell selector centers each content area inside a fixed 960px width.
- The .site-header selector adds a subtle dividing line.
- The .nav-wrap selector places the brand opposite the navigation.
- The .brand selector gives the business name a stronger visual weight.
- Save styles.css.
- Refresh the page in Google Chrome.
You'll see the business name move into a bordered header. The page content now sits inside a centered desktop column.
Is the header still stacked?
Check that .nav-wrap starts with a full stop. Confirm that its opening and closing braces surround every layout property.
Still stuck? Help me debug why my .nav-wrap header is not using a horizontal flex layout.
The header has its main layout. The navigation list still needs its own spacing and interaction styles.
- Append the navigation rules to styles.css by pasting this code below the .brand rule:
.nav-links {
display: flex;
gap: 1.25rem;
margin: 0;
padding: 0;
list-style: none;
}
.nav-links a {
color: var(--muted);
font-weight: 700;
text-decoration: none;
}
.nav-links a:hover,
.nav-links a:focus-visible {
color: var(--brand);
}
What changes in the navigation?
- The .nav-links selector removes the list markers.
- Its flex layout places the three links in one row.
- The link rules create a muted default appearance.
- The interaction rule changes a link to the brand color during mouse or keyboard focus.
- Save styles.css.
- Refresh the page in Google Chrome.
- Move your pointer over each navigation link.
You'll see Services, Process, and Contact arranged in one row. Each link changes from muted gray to green when you point at it.
Do the navigation links still have bullets?
Confirm that list-style: none sits inside the .nav-links rule. Check that the HTML list uses the matching nav-links class.
Still stuck? Help me find why my nav-links list still shows bullets or vertical links.
Style the hero and calls to action
The hero carries the service promise that visitors see first. Strong spacing and a soft background make that message feel intentional.
- Append the hero layout rules to styles.css by pasting this code below the navigation rules:
.hero {
padding: 6rem 0;
background: linear-gradient(135deg, #e4f2ef 0%, #fff7e6 100%);
}
.hero-content {
max-width: 52rem;
}
.eyebrow {
margin: 0 0 0.75rem;
color: var(--brand);
font-size: 0.8rem;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
How does the hero create focus?
- The .hero selector creates generous vertical space around the opening message.
- The gradient separates the hero from the white sections below.
- The .hero-content selector limits the text line length.
- The .eyebrow selector turns small section labels into green uppercase markers.
- Save styles.css.
- Refresh the page in Google Chrome.
You'll see a green-to-cream hero background with more vertical space. The small service label now appears in uppercase green text.
Is the hero background missing?
Check that the .hero selector matches the class on the opening section element. Confirm that the full gradient value remains inside the rule.
Still stuck? Help me debug why the .hero gradient and spacing are not appearing.
The hero now has a clear background and section label. Its headings need a stronger scale so visitors can scan the promise quickly.
- Append the heading and hero-copy rules to styles.css by pasting this code below the .eyebrow rule:
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
max-width: 48rem;
margin-bottom: 1.25rem;
font-size: clamp(2.5rem, 7vw, 4.75rem);
line-height: 1.05;
}
h2 {
max-width: 44rem;
margin-bottom: 2rem;
font-size: clamp(2rem, 4vw, 3rem);
line-height: 1.15;
}
h3 {
margin-bottom: 0.5rem;
}
.hero-copy {
max-width: 42rem;
color: var(--muted);
font-size: 1.15rem;
}
Why use a fluid heading scale?
- The shared rule removes the browser's default top margins from headings and paragraphs.
- The clamp() values let major headings scale within set limits.
- The line-height values keep large headings compact.
- The .hero-copy selector gives the supporting message a softer color and readable width.
- Save styles.css.
- Refresh the page in Google Chrome.
You'll see a large service promise at the top of the page. The supporting paragraph now appears smaller and softer beneath it.
Are the headings still small?
Confirm that the heading selectors use plain element names such as h1 and h2. Check that every clamp() value includes its closing parenthesis.
Still stuck? Help me find why my h1 and h2 font sizes are not changing.
The headline now leads the visual hierarchy. The two links beneath it need a shared button shape before their individual colors can stand apart.
- Append the shared button layout rules to styles.css by pasting this code below the .hero-copy rule:
.button-row {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-top: 2rem;
}
.button {
display: inline-block;
padding: 0.8rem 1.1rem;
border: 2px solid var(--brand);
border-radius: 0.6rem;
font-weight: 800;
text-decoration: none;
}
What creates the button shape?
- The .button-row selector places the two calls to action beside each other.
- Its wrapping behavior protects the row when space becomes limited.
- The .button selector gives both links consistent padding, borders, rounded corners, and heavier text.
- Save styles.css.
- Refresh the page in Google Chrome.
You'll see View services and Request a quote presented as bordered controls. Both buttons share the same size and rounded shape.
Do the links still look like plain text?
Confirm that each hero link has the button class in index.html. Check that the CSS selector begins with .button.
Still stuck? Help me debug why my button class is not styling the hero links.
The two links now share one visual shape. Their color variants show which action deserves the strongest attention.
- Append the button color rules to styles.css by pasting this code below the .button rule:
.button-primary {
color: #ffffff;
background: var(--brand);
}
.button-primary:hover,
.button-primary:focus-visible {
background: var(--brand-dark);
border-color: var(--brand-dark);
}
.button-secondary {
color: var(--brand-dark);
background: transparent;
}
How do the button variants work?
- The .button-primary selector creates a solid green action.
- Its interaction rule darkens the primary button during mouse or keyboard focus.
- The .button-secondary selector keeps the alternative action transparent.
- Save styles.css.
- Refresh the page in Google Chrome.
- Move your pointer over the View services button.
You'll see a solid green primary button beside a transparent secondary button. The primary button darkens when you point at it.
Are both buttons still transparent?
Confirm that the first hero link includes the button-primary class. Check that the CSS selector includes the leading full stop.
Still stuck? Help me find why my button-primary class is not applying its green background.
Finish the sections and test the viewport
The hero now creates a strong first impression. The service cards and process steps need a matching structure so the rest of the page feels equally deliberate.
- Append the section and card rules to styles.css by pasting this code below the button rules:
.section {
padding: 4.5rem 0;
}
.section-muted {
background: var(--surface-soft);
}
.card-grid,
.process-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1rem;
}
.card {
padding: 1.5rem;
border: 1px solid var(--line);
border-radius: 1rem;
background: var(--surface);
box-shadow: var(--shadow);
}
.card p,
.process-grid p,
.contact-panel p,
footer p {
color: var(--muted);
}
How do the content sections become scannable?
- The .section selector creates consistent vertical spacing.
- The .section-muted selector separates the process section with a pale background.
- The shared grid rule divides the services and process into three equal columns.
- The .card selector adds a border, rounded corners, and a soft shadow.
- The grouped paragraph rule softens supporting copy throughout the lower sections.
- Save styles.css.
- Refresh the page in Google Chrome.
You'll see three service cards arranged in one row. The Process section now has a pale background with three columns.
Are the cards still in one vertical list?
Check that both .card-grid and .process-grid appear before the shared opening brace. Confirm that the grid declaration sits inside that rule.
Still stuck? Help me debug why my card-grid and process-grid are not showing three columns.
The grids now organize the major offerings and process stages. Number badges make the sequence easier to follow at a glance.
- Append the process-list rules to styles.css by pasting this code below the grouped paragraph rule:
.process-grid {
margin: 0;
padding: 0;
list-style: none;
}
.process-grid li {
padding: 1.25rem;
}
.process-grid span {
display: inline-grid;
place-items: center;
width: 2.25rem;
height: 2.25rem;
margin-bottom: 1rem;
border-radius: 50%;
color: var(--brand-dark);
background: var(--accent);
font-weight: 900;
}
What makes the process feel sequential?
- The .process-grid selector removes the ordered-list markers and default spacing.
- Each process item receives padding around its content.
- The .process-grid span selector turns each number into a centered circular badge.
- Save styles.css.
- Refresh the page in Google Chrome.
You'll see a gold circular number above each process step. The browser's default ordered-list markers are gone.
Are the process numbers still plain?
Confirm that the numbered elements are span elements inside .process-grid. Check that the badge selector includes a space before span.
Still stuck? Help me find why the process-grid span numbers are not becoming circular badges.
The process now reads as a clear three-step path. The contact panel and footer complete the desktop presentation.
- Append the contact and footer rules to styles.css by pasting this code below the process rules:
.contact-panel {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
padding: 2rem;
border-radius: 1rem;
background: var(--surface-soft);
}
.contact-panel h2 {
margin-bottom: 0.75rem;
}
footer {
padding: 2rem 0;
border-top: 1px solid var(--line);
}
footer p {
margin-bottom: 0;
}
How does the page finish?
- The .contact-panel selector places the contact message opposite the quote button.
- Its pale background and rounded corners turn the contact area into a distinct call to action.
- The footer rule adds breathing room and a subtle top border.
- The final paragraph rule removes extra space below the portfolio note.
- Save styles.css.
- Refresh the page in Google Chrome.
You'll see the contact message and Request a quote button inside one pale rounded panel. The footer now ends the page with a thin divider.
Is the contact button below the message?
Confirm that display: flex sits inside the .contact-panel rule. Check that the contact section still uses the matching contact-panel class in index.html.
Still stuck? Help me debug why my contact-panel is not placing its content in one row.
✔️ Awesome, I've got everything!
Your desktop demo now has the complete fixed-width styling. Double check that styles.css is saved before testing it at a narrow width.
ⓧ I'd like to double check the full code
Compare your styles.css file with this complete desktop version. It ends after the footer rules.
:root {
--ink: #172033;
--muted: #5b6474;
--surface: #ffffff;
--surface-soft: #eef4f3;
--brand: #12645a;
--brand-dark: #0b4942;
--accent: #f2b84b;
--line: #dce5e3;
--shadow: 0 18px 45px rgba(23, 32, 51, 0.1);
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
color: var(--ink);
background: var(--surface);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.6;
}
a {
color: inherit;
}
.site-shell {
width: 960px;
margin-inline: auto;
}
.site-header {
border-bottom: 1px solid var(--line);
background: rgba(255, 255, 255, 0.96);
}
.nav-wrap {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 4.5rem;
gap: 1.5rem;
}
.brand {
color: var(--brand-dark);
font-weight: 800;
text-decoration: none;
}
.nav-links {
display: flex;
gap: 1.25rem;
margin: 0;
padding: 0;
list-style: none;
}
.nav-links a {
color: var(--muted);
font-weight: 700;
text-decoration: none;
}
.nav-links a:hover,
.nav-links a:focus-visible {
color: var(--brand);
}
.hero {
padding: 6rem 0;
background: linear-gradient(135deg, #e4f2ef 0%, #fff7e6 100%);
}
.hero-content {
max-width: 52rem;
}
.eyebrow {
margin: 0 0 0.75rem;
color: var(--brand);
font-size: 0.8rem;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
max-width: 48rem;
margin-bottom: 1.25rem;
font-size: clamp(2.5rem, 7vw, 4.75rem);
line-height: 1.05;
}
h2 {
max-width: 44rem;
margin-bottom: 2rem;
font-size: clamp(2rem, 4vw, 3rem);
line-height: 1.15;
}
h3 {
margin-bottom: 0.5rem;
}
.hero-copy {
max-width: 42rem;
color: var(--muted);
font-size: 1.15rem;
}
.button-row {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-top: 2rem;
}
.button {
display: inline-block;
padding: 0.8rem 1.1rem;
border: 2px solid var(--brand);
border-radius: 0.6rem;
font-weight: 800;
text-decoration: none;
}
.button-primary {
color: #ffffff;
background: var(--brand);
}
.button-primary:hover,
.button-primary:focus-visible {
background: var(--brand-dark);
border-color: var(--brand-dark);
}
.button-secondary {
color: var(--brand-dark);
background: transparent;
}
.section {
padding: 4.5rem 0;
}
.section-muted {
background: var(--surface-soft);
}
.card-grid,
.process-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1rem;
}
.card {
padding: 1.5rem;
border: 1px solid var(--line);
border-radius: 1rem;
background: var(--surface);
box-shadow: var(--shadow);
}
.card p,
.process-grid p,
.contact-panel p,
footer p {
color: var(--muted);
}
.process-grid {
margin: 0;
padding: 0;
list-style: none;
}
.process-grid li {
padding: 1.25rem;
}
.process-grid span {
display: inline-grid;
place-items: center;
width: 2.25rem;
height: 2.25rem;
margin-bottom: 1rem;
border-radius: 50%;
color: var(--brand-dark);
background: var(--accent);
font-weight: 900;
}
.contact-panel {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
padding: 2rem;
border-radius: 1rem;
background: var(--surface-soft);
}
.contact-panel h2 {
margin-bottom: 0.75rem;
}
footer {
padding: 2rem 0;
border-top: 1px solid var(--line);
}
footer p {
margin-bottom: 0;
}
The desktop page now looks ready to show. Before you test it, do you think the same layout will fit inside a 375px viewport?
- Return to the Northstar Home Services page in Google Chrome.
- Open Chrome DevTools for the page from Chrome's browser menu.
- Select the Device Toolbar control in the Chrome DevTools toolbar.
- Choose Mobile M from the viewport presets.
- Confirm that the viewport width is 375px.
You'll see the 960px page extend beyond the narrow viewport. A horizontal scrollbar exposes the desktop-only layout.
- Drag the horizontal scrollbar to reveal the content extending past the right edge.
Why is this overflow useful?
The fixed 960px shell creates a polished desktop layout. The Mobile M check proves that a fixed desktop width cannot fit inside a 375px viewport.
This visible shortfall gives you a concrete responsive-design problem to solve next.
Can't see the horizontal overflow?
Confirm that Mobile M is selected in the Device Toolbar. Check that .site-shell still uses width: 960px.
Still stuck? Help me verify the fixed-width overflow in Chrome DevTools at the Mobile M viewport.
You now have a polished desktop demo and visible proof of its narrow-screen limitation. Next, you'll replace the fixed shell with fluid sizing and reorganize the layout for mobile visitors.
Make the Website Responsive
The narrow viewport in Chrome DevTools exposed the weak point in your polished page. The fixed shell forces mobile visitors to scroll sideways before they can read the content.
You will use responsive design to make the shell shrink with the screen. A mobile media query will reorganize the crowded sections into a readable layout.
In this step, get ready to:
- Replace the fixed shell with fluid sizing.
- Add a mobile breakpoint that reorganizes the page.
- Test every interactive link across the responsive layout.
Replace the fixed shell
A fluid shell follows the viewport width while preserving space around the content. A maximum width keeps the desktop layout from stretching too far on a large screen.
- Switch back to styles.css in Visual Studio Code.
- Find the .site-shell rule containing width: 960px.
- Replace the entire .site-shell rule with this code:
.site-shell {
width: calc(100% - 2rem);
max-width: 68rem;
margin-inline: auto;
}
What Does This Rule Do?
- The calc(100% - 2rem) width lets the shell shrink with the viewport. It also leaves space beside the content.
- The max-width: 68rem declaration limits the shell on wider screens.
- The margin-inline: auto declaration keeps the shell centered.
Before you refresh, consider whether the horizontal scrollbar will disappear once the shell can shrink.
- Save styles.css.
- Return to the Northstar Home Services page in Google Chrome.
- Select Mobile M in the Device Toolbar.
- Refresh the page.
You will see the page fit inside the viewport without the fixed shell pushing past the right edge. The three-column sections still look cramped because their layout has not reorganized for mobile yet.
Still Seeing the Fixed-Width Page?
Check that width: 960px no longer appears inside the .site-shell rule. Confirm that you saved styles.css before refreshing the browser.
Still stuck? Help me find why my fluid .site-shell CSS is not removing horizontal overflow.
Add the mobile layout
A media query applies a group of CSS rules only when a viewport matches a condition. This breakpoint changes the page when its width reaches 700px or less.
- Switch back to styles.css.
- Scroll to the bottom of the file.
- Add this media query below the footer p rule:
@media (max-width: 700px) {
.nav-wrap,
.contact-panel {
align-items: flex-start;
flex-direction: column;
}
.nav-wrap {
padding: 1rem 0;
}
.nav-links {
flex-wrap: wrap;
}
.hero {
padding: 4rem 0;
}
.card-grid,
.process-grid {
grid-template-columns: 1fr;
}
}
How Does the Breakpoint Help?
- The navigation wrapper and contact panel switch to vertical layouts. Their content gains room to breathe on a narrow screen.
- The navigation links can wrap onto another line when needed.
- The hero uses smaller vertical spacing to keep its main message close to the top.
- The service cards and process steps switch from three columns to one 1fr column.
Before you refresh, consider which sections will change from horizontal arrangements to vertical arrangements.
- Save styles.css.
- Return to the page at the Mobile M viewport.
- Refresh the page.
You will see the services and process steps in single columns. The navigation can wrap while the contact panel places its button below the text.
Mobile Rules Not Appearing?
Confirm that the media query sits after the closing brace for the footer p rule. Check that every opening brace in the media query has a matching closing brace.
Still stuck? Help me debug why my 700px CSS media query is not changing the mobile layout.
✔️ Awesome, I've got everything!
- Confirm that styles.css is saved.
- Confirm that the mobile page uses single-column service and process sections.
ⓧ I'd like to double check the full code
The files below match the responsive project checkpoint. Compare each file with the version in your local-business-demo folder.
styles.css
:root {
--ink: #172033;
--muted: #5b6474;
--surface: #ffffff;
--surface-soft: #eef4f3;
--brand: #12645a;
--brand-dark: #0b4942;
--accent: #f2b84b;
--line: #dce5e3;
--shadow: 0 18px 45px rgba(23, 32, 51, 0.1);
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
margin: 0;
color: var(--ink);
background: var(--surface);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
line-height: 1.6;
}
a {
color: inherit;
}
.site-shell {
width: calc(100% - 2rem);
max-width: 68rem;
margin-inline: auto;
}
.site-header {
border-bottom: 1px solid var(--line);
background: rgba(255, 255, 255, 0.96);
}
.nav-wrap {
display: flex;
align-items: center;
justify-content: space-between;
min-height: 4.5rem;
gap: 1.5rem;
}
.brand {
color: var(--brand-dark);
font-weight: 800;
text-decoration: none;
}
.nav-links {
display: flex;
gap: 1.25rem;
margin: 0;
padding: 0;
list-style: none;
}
.nav-links a {
color: var(--muted);
font-weight: 700;
text-decoration: none;
}
.nav-links a:hover,
.nav-links a:focus-visible {
color: var(--brand);
}
.hero {
padding: 6rem 0;
background: linear-gradient(135deg, #e4f2ef 0%, #fff7e6 100%);
}
.hero-content {
max-width: 52rem;
}
.eyebrow {
margin: 0 0 0.75rem;
color: var(--brand);
font-size: 0.8rem;
font-weight: 800;
letter-spacing: 0.12em;
text-transform: uppercase;
}
h1,
h2,
h3,
p {
margin-top: 0;
}
h1 {
max-width: 48rem;
margin-bottom: 1.25rem;
font-size: clamp(2.5rem, 7vw, 4.75rem);
line-height: 1.05;
}
h2 {
max-width: 44rem;
margin-bottom: 2rem;
font-size: clamp(2rem, 4vw, 3rem);
line-height: 1.15;
}
h3 {
margin-bottom: 0.5rem;
}
.hero-copy {
max-width: 42rem;
color: var(--muted);
font-size: 1.15rem;
}
.button-row {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
margin-top: 2rem;
}
.button {
display: inline-block;
padding: 0.8rem 1.1rem;
border: 2px solid var(--brand);
border-radius: 0.6rem;
font-weight: 800;
text-decoration: none;
}
.button-primary {
color: #ffffff;
background: var(--brand);
}
.button-primary:hover,
.button-primary:focus-visible {
background: var(--brand-dark);
border-color: var(--brand-dark);
}
.button-secondary {
color: var(--brand-dark);
background: transparent;
}
.section {
padding: 4.5rem 0;
}
.section-muted {
background: var(--surface-soft);
}
.card-grid,
.process-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 1rem;
}
.card {
padding: 1.5rem;
border: 1px solid var(--line);
border-radius: 1rem;
background: var(--surface);
box-shadow: var(--shadow);
}
.card p,
.process-grid p,
.contact-panel p,
footer p {
color: var(--muted);
}
.process-grid {
margin: 0;
padding: 0;
list-style: none;
}
.process-grid li {
padding: 1.25rem;
}
.process-grid span {
display: inline-grid;
place-items: center;
width: 2.25rem;
height: 2.25rem;
margin-bottom: 1rem;
border-radius: 50%;
color: var(--brand-dark);
background: var(--accent);
font-weight: 900;
}
.contact-panel {
display: flex;
align-items: center;
justify-content: space-between;
gap: 2rem;
padding: 2rem;
border-radius: 1rem;
background: var(--surface-soft);
}
.contact-panel h2 {
margin-bottom: 0.75rem;
}
footer {
padding: 2rem 0;
border-top: 1px solid var(--line);
}
footer p {
margin-bottom: 0;
}
@media (max-width: 700px) {
.nav-wrap,
.contact-panel {
align-items: flex-start;
flex-direction: column;
}
.nav-wrap {
padding: 1rem 0;
}
.nav-links {
flex-wrap: wrap;
}
.hero {
padding: 4rem 0;
}
.card-grid,
.process-grid {
grid-template-columns: 1fr;
}
}
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Northstar Home Services</title>
<meta
name="description"
content="A portfolio demo for a clear, responsive local-business website."
>
<link rel="stylesheet" href="styles.css">
</head>
<body id="top">
<header class="site-header">
<div class="site-shell nav-wrap">
<a class="brand" href="#top">Northstar Home Services</a>
<nav aria-label="Primary navigation">
<ul class="nav-links">
<li><a href="#services">Services</a></li>
<li><a href="#process">Process</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</nav>
</div>
</header>
<main>
<section class="hero">
<div class="site-shell hero-content">
<p class="eyebrow">Friendly help for busy homeowners</p>
<h1>Reliable help for every corner of your home.</h1>
<p class="hero-copy">
Northstar Home Services handles everyday repairs, seasonal upkeep,
and small improvement projects with clear communication from start
to finish.
</p>
<div class="button-row">
<a class="button button-primary" href="#services">View services</a>
<a class="button button-secondary" href="#contact">Request a quote</a>
</div>
</div>
</section>
<section class="section" id="services" aria-labelledby="services-title">
<div class="site-shell">
<p class="eyebrow">Services</p>
<h2 id="services-title">Practical support without the runaround</h2>
<div class="card-grid">
<article class="card">
<h3>Small repairs</h3>
<p>Help with shelves, doors, fixtures, and the jobs that keep getting postponed.</p>
</article>
<article class="card">
<h3>Seasonal upkeep</h3>
<p>Simple maintenance visits that help prepare a home for the months ahead.</p>
</article>
<article class="card">
<h3>Room refreshes</h3>
<p>Focused improvements that make an everyday space feel cleaner and more useful.</p>
</article>
</div>
</div>
</section>
<section class="section section-muted" id="process" aria-labelledby="process-title">
<div class="site-shell">
<p class="eyebrow">Process</p>
<h2 id="process-title">A simple path from question to completion</h2>
<ol class="process-grid">
<li>
<span>1</span>
<h3>Share the job</h3>
<p>Describe the task, timing, and any photos that make the request clearer.</p>
</li>
<li>
<span>2</span>
<h3>Confirm the plan</h3>
<p>Agree on the scope, schedule, and price before the work begins.</p>
</li>
<li>
<span>3</span>
<h3>Get it finished</h3>
<p>Review the completed work and ask any final questions before sign-off.</p>
</li>
</ol>
</div>
</section>
<section class="section" id="contact" aria-labelledby="contact-title">
<div class="site-shell contact-panel">
<div>
<p class="eyebrow">Contact</p>
<h2 id="contact-title">Ready to discuss the next small project?</h2>
<p>Replace the sample email with the address you want potential clients to use.</p>
</div>
<a class="button button-primary" href="mailto:your-email@example.com">Request a quote</a>
</div>
</section>
</main>
<footer>
<div class="site-shell">
<p>Portfolio demo created to showcase a one-page website service.</p>
</div>
</footer>
</body>
</html>
Test every viewport and link
Responsive testing covers the widths between familiar device presets. Dragging the viewport exposes layout problems that a single screenshot can miss.
Before you begin the final check, consider whether the page will remain readable at every width between Mobile M and desktop.
- Select Mobile M in the Device Toolbar.
- Confirm that the viewport is 375px wide.
- Drag the right edge of the viewport toward a desktop width.
- Drag the right edge back toward 375px.
You will see the layout move smoothly between three-column and single-column sections. No horizontal scrollbar appears at any tested width.
- Return the viewport to Mobile M.
- Click the Services navigation link.
- Click the Process navigation link.
- Click the Contact navigation link.
Each navigation link moves you to its matching section. The destination remains readable within the mobile viewport.
- Click the Request a quote button in the contact section.
Your browser will open a draft in the configured email app or ask you to choose one. The draft uses the address from the button link without sending a message.
- Close the draft without sending it.
- Turn off the Device Toolbar to return to desktop view.
- Click the Services navigation link.
- Click the Process navigation link.
- Click the Contact navigation link.
- Click the Request a quote button.
The navigation links reach the same sections in desktop view. The email button starts the same type of draft.
- Close the draft without sending it.
- Before using the demo for outreach, replace your-email@example.com in index.html with your business email.
Email Button Not Opening a Draft?
Your computer needs an email app configured to open email links. If Chrome asks you to choose an app, select the email app you normally use.
Still stuck? Help me verify the mailto link on my Request a quote button.
That mobile failure is resolved. Your Northstar Home Services demo now stays readable across viewport sizes and is ready for a public link in the next step.
Publish and Package the Service
Your responsive Northstar Home Services page now works on desktop screens. It also works on mobile screens.
The page still lives on your Mac, so a potential client cannot evaluate it from a link. GitHub stores the files in a public repository.
GitHub Pages turns those files into a public demo. Google Docs turns the finished work into a clear service offer.
In this step, get ready to:
- Create a public GitHub repository containing the two website files.
- Publish the responsive demo with GitHub Pages.
- Package the demo as a $99 introductory service offer.
Create the public repository
A public repository gives your finished website a permanent home online. It also provides the source that GitHub Pages publishes.
- Switch back to the regular browser view in Google Chrome.
- Sign in to GitHub with your verified personal account.
- Select New repository.
- Enter local-business-demo as the repository name.
- Choose the public visibility option.
- Turn Add a README file on.
- Click Create repository.
You should see the new local-business-demo repository with a README on the main branch.
- Click Add file.
- Select Upload files.
- Select the two project files from your local-business-demo folder: index.html plus styles.css.
- Select Commit directly to the main branch.
- Click Commit changes.
The repository should now list index.html plus styles.css on the main branch.
Why GitHub Pages?
GitHub Pages publishes static files directly from a repository.
Your site consists of index.html plus styles.css. A server-based host would add setup that these files do not need.
A public repository keeps this project at $0 on GitHub Free.
Files missing from the repository?
- Confirm that you selected both index.html plus styles.css from the local local-business-demo folder.
- Confirm that the branch selector shows main.
- Repeat the upload if either file is absent from the repository page.
Need another pair of eyes? Help me fix my GitHub file upload.
Publish with GitHub Pages
Branch publishing takes the files on main and serves them as a public website. The repository remains the source for every future update.
- Open Settings in the repository.
- Select Pages.
- Choose Deploy from a branch as the publishing source.
- Select main as the branch.
- Select /(root) as the folder.
- Click Save.
Publishing can take up to 10 minutes. The wait is expected while GitHub Pages prepares the deployment.
Before you open the site, predict whether the public version will match the responsive page on your Mac.
- Click Visit site when the publishing status shows that the site is ready.
You should see the complete Northstar Home Services page at a public URL. The navigation, service cards, process section, contact panel, and footer should match your local version.
- Record the address bar value here: your public live demo URL.
Visit site not ready?
- Return to Settings.
- Select Pages.
- Confirm that the publishing source shows main with /(root).
- Wait for the deployment to finish if the publishing status is still processing.
Still blocked? Help me diagnose my GitHub Pages deployment.
Package the offer and verify access
A fixed-price starter offer gives the project clear boundaries. A potential client can see the result before deciding whether the package fits their business.
- Go to Google Docs in Google Chrome using your Google account.
- Click Create.
- Name the document One-Page Starter Website Offer.
- Add an introductory fixed price of $99 near the top.
- State that the package includes one responsive page.
- Add the included sections: Hero, Services, Process, and Contact.
- State that the package includes one revision.
- List these required client inputs: business name, service details, contact details, brand colours, and a logo if available.
The offer now defines the price, scope, revision limit, and information needed to begin.
- Add the outreach message shown below to the end of the offer.
Outreach Message
Hi recipient name, I build simple mobile-friendly pages that help local businesses show their services and contact details clearly. Here is a sample I built: your public live demo URL.
My introductory one-page package is $99 and includes one revision. The message ends with Would this be useful for your business?.
Choose the sharing path that matches the access options available in your Google account.
✔️ Anyone with the link is available
Use this path when public link sharing is available for your Google account.
- Open the document sharing controls.
- Under General access, choose Anyone with the link.
- Select Viewer.
- Click Copy link.
- Record the copied value here: your service offer viewer link.
- Click Done.
ⓧ Anyone with the link is blocked
Managed Google accounts can block public link sharing. A named viewer gives you a restricted fallback.
- Add a specific Google account that you can access in a private browser window.
- Set that account to Viewer.
- Click Copy link.
- Record the copied value here: your service offer viewer link.
- Click Done.
Before you test both links, predict whether a potential client will need edit access to view either resource.
- Open a private browser window in Google Chrome.
- Paste your public live demo URL into the address bar.
You should see the responsive Northstar Home Services site without signing in to GitHub.
- Paste your service offer viewer link into the same private window.
- Sign in with the specific Viewer account if you used the managed-account fallback.
You should see the complete offer with Viewer access. The document should not provide edit permission.
One of the links is inaccessible?
- Confirm that the GitHub repository visibility is public if the live demo cannot load.
- Confirm that Google Docs shows Anyone with the link with Viewer access if the offer requests permission.
- Confirm that the private window uses the specific Viewer account if your managed Google account blocks public links.
Need help checking access? Help me troubleshoot my public demo and viewer-only offer links.
That's the business-ready handoff complete. A potential client can now open your live sample and read exactly what the $99 package includes.
Secret mission
Add a Website Audit Upsell
Run a focused Lighthouse report on a public local-business website. Turn three prioritized findings into a client-facing audit and a $25 Website Checkup add-on.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project has no ongoing cost, with the public GitHub Pages deployment using GitHub Free at $0 USD per month.
Resources you used:
- Local files: the local-business-demo folder containing index.html plus styles.css.
- GitHub: the public local-business-demo repository with its GitHub Pages deployment.
- Google Docs: the viewer-access One-Page Starter Website Offer document.
- Google Docs: the client-facing Website Audit document.
Keep everything running
No action needed. Choose this if you are ready to use the live demo plus its supporting documents for outreach.
- Keep the public GitHub Pages link available for potential clients.
- Keep the One-Page Starter Website Offer shared with Viewer access.
- Keep the Website Audit available as evidence for your website checkup service.
- Keep the local local-business-demo folder available for future edits.
Your live sample, service offer, and audit remain ready for your first outreach conversation.
Pause - I'll come back to this later
This option removes public access to the website while preserving the repository files plus both documents.
- Return to the local-business-demo repository on GitHub.
- Select Settings.
- Select Pages in the sidebar.
- Open the menu beside Your site is live at.
- Select Unpublish site.
You have made a clean pause. The public site is offline while the source files plus both Google Docs remain ready for your return.
Delete - I don't want to use this again
Permanent deletion can feel drastic. Each group below targets one resource so you can protect anything you still need.
- Save any copy you want to keep before continuing.
Delete the GitHub repository:
- Return to Settings for the public local-business-demo repository.
- Open the repository's general settings page.
- Scroll to the repository deletion controls at the bottom of the page.
- Choose the control that permanently deletes the repository.
- Complete GitHub's confirmation prompts.
- Return to your repository list to confirm local-business-demo is absent.
Deleting the repository also removes its GitHub Pages deployment.
Delete the local folder:
- Use your Mac's file search to locate the local-business-demo folder.
- Move the local-business-demo folder to Trash.
- Review Trash for any unrelated files you still need.
- Empty your Mac's Trash.
- Search for local-business-demo again to confirm no matching folder remains.
Move the Google Docs files to Trash:
- Return to the Google Docs home screen.
- Select One-Page Starter Website Offer.
- Move the selected document to Trash.
- Select Website Audit.
- Move the selected document to Trash.
- Open Trash from the Google Docs home screen.
You should see both documents in Trash. Your active workspace now contains none of the project resources.
Nice Work!
Nice Work!
You did it! You built a responsive one-page business website from a blank setup page.
You now have a live sample on GitHub Pages. Your $99 viewer-access offer gives you a clear service to share with potential clients.
You've learned how to:
- Built the Northstar Home Services page with semantic HTML. Used CSS to turn it into a polished example of responsive design.
- Exposed a fixed-width mobile failure in Chrome DevTools at the Mobile M viewport. Repaired it with fluid sizing. Added a media query for readable narrow-screen sections.
- Published the finished site through GitHub Pages. Packaged the work in Google Docs as a $99 introductory service with a client-ready outreach message.
- Completed the Secret Mission by turning Lighthouse findings into a three-finding website audit. Added it as a $25 Website Checkup upsell with a clear client-facing scope.
Ready to quiz yourself?