This is the starting point of the series. You will create an Angular 19 project named angulartailwindtutorial1, add Tailwind CSS 4, and build a navbar, a hero section, and three routed pages. Later tutorials (the accessible contact form, NgRx state, and the Node API search) build on this project.
What you need
- Node.js 20 or newer and npm (check with
node -vandnpm -v). - A code editor such as VS Code and a terminal.
1. Create the project
Use the Angular 19 CLI explicitly so the generated files match this tutorial, even if a newer CLI is installed globally.
npx @angular/cli@19 new angulartailwindtutorial1 --style=css --routing --ssr=false cd angulartailwindtutorial1
The flags mean: --style=css uses plain CSS (Tailwind works best with it), --routing creates src/app/app.routes.ts, and --ssr=false skips server-side rendering to keep this 101 simple.
2. Run the app
npx ng serve
Open http://localhost:4200. The dev server rebuilds when you save a file. Stop it with Ctrl+C. To produce a production bundle, run npx ng build; the output goes to dist/.
3. Add Tailwind CSS 4
Tailwind 4 runs as a PostCSS plugin. Install it:
npm install tailwindcss @tailwindcss/postcss postcss
Create .postcssrc.json in the project root (next to package.json):
{
"plugins": {
"@tailwindcss/postcss": {}
}
}
Replace the contents of src/styles.css with:
@import "tailwindcss";
Restart ng serve so Angular picks up the PostCSS configuration. Tailwind 4 needs no tailwind.config.js for a basic setup, and it scans your templates automatically.
4. Clear the starter page
Open src/app/app.component.html and replace everything with the navbar and the router outlet:
<app-navbar></app-navbar> <router-outlet></router-outlet>
router-outlet is where Angular renders the component for the current URL.
5. Generate the components
npx ng generate component navbar npx ng generate component hero npx ng generate component about npx ng generate component contact
In Angular 19 these are standalone components by default, and each file is named like navbar.component.ts. Note: if you follow along with Angular 20, the CLI drops the .component suffix (navbar.ts, class Navbar). The code below is otherwise the same.
6. Build the navbar
In src/app/navbar/navbar.component.ts, import RouterLink and RouterLinkActive so the template can use them:
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-navbar',
imports: [RouterLink, RouterLinkActive],
templateUrl: './navbar.component.html',
})
export class NavbarComponent {}
Then src/app/navbar/navbar.component.html:
<nav class="bg-white shadow-md" aria-label="Main">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div class="flex h-16 items-center justify-between">
<a routerLink="/" class="text-xl font-semibold text-blue-700 hover:text-blue-900">
Surreal Scapes
</a>
<div class="flex gap-6">
<a routerLink="/" routerLinkActive="underline" [routerLinkActiveOptions]="{ exact: true }"
class="font-semibold text-gray-700 hover:text-blue-700">Home</a>
<a routerLink="/about" routerLinkActive="underline"
class="font-semibold text-gray-700 hover:text-blue-700">About</a>
<a routerLink="/contact" routerLinkActive="underline"
class="font-semibold text-gray-700 hover:text-blue-700">Contact</a>
</div>
</div>
</div>
</nav>
Class breakdown: mx-auto max-w-7xl centers and limits the width; px-4 sm:px-6 lg:px-8 increases side padding at wider breakpoints; flex items-center justify-between puts the logo on the left and links on the right; hover: styles apply on mouse hover. aria-label="Main" names the navigation landmark for screen readers, and routerLinkActive underlines the current page so the active link is not shown by color alone.
7. Build the hero section
This version uses a CSS gradient instead of an image, so the project has no asset dependencies. In src/app/hero/hero.component.ts:
import { Component } from '@angular/core';
import { RouterLink } from '@angular/router';
@Component({
selector: 'app-hero',
imports: [RouterLink],
templateUrl: './hero.component.html',
})
export class HeroComponent {}
And src/app/hero/hero.component.html:
<section class="flex h-[70vh] items-center justify-center bg-gradient-to-br from-blue-900 to-slate-800 px-4 text-center text-white">
<div>
<h1 class="mb-4 text-4xl font-bold sm:text-5xl">Art Gallery</h1>
<p class="mx-auto mb-6 max-w-2xl text-lg sm:text-xl">Explore exquisite art collections</p>
<a routerLink="/contact"
class="rounded-lg bg-white px-6 py-3 font-semibold text-blue-900 transition hover:bg-blue-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white">
Contact Us
</a>
</div>
</section>
h-[70vh] is an arbitrary value: 70 percent of the viewport height. The button uses dark text on a white background for strong contrast, and focus-visible: adds a clear outline for keyboard users.
8. Add the pages and routes
Give the About and Contact components simple content for now. In src/app/about/about.component.html:
<main class="mx-auto max-w-3xl px-4 py-10"> <h1 class="mb-4 text-3xl font-bold">About</h1> <p class="text-gray-700">This gallery is a practice project for Angular and Tailwind CSS.</p> </main>
In src/app/contact/contact.component.html:
<main class="mx-auto max-w-3xl px-4 py-10"> <h1 class="mb-4 text-3xl font-bold">Contact</h1> <p class="text-gray-700">The contact form is built in the next tutorial.</p> </main>
Now map URLs to components in src/app/app.routes.ts. The About page is lazy loaded, so its code is only downloaded when the user visits it:
import { Routes } from '@angular/router';
import { HeroComponent } from './hero/hero.component';
import { ContactComponent } from './contact/contact.component';
export const routes: Routes = [
{ path: '', component: HeroComponent },
{
path: 'about',
loadComponent: () => import('./about/about.component').then((m) => m.AboutComponent),
},
{ path: 'contact', component: ContactComponent },
];
Finally, make sure src/app/app.component.ts imports what its template uses:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { NavbarComponent } from './navbar/navbar.component';
@Component({
selector: 'app-root',
imports: [RouterOutlet, NavbarComponent],
templateUrl: './app.component.html',
})
export class AppComponent {}
9. Verify
With npx ng serve running, check that the navbar appears on every page, the three links change the URL without a full reload, and the active link is underlined. Resize the browser to confirm the hero text stays readable. Run npx ng build to confirm there are no compile errors.
Common problems
- Tailwind classes have no effect: confirm
.postcssrc.jsonis in the project root andsrc/styles.csscontains the@import, then restartng serve. - “is not a known element” error: the component is missing from the
importsarray of the component that uses it. - Blank page on refresh at
/about: this is a dev server or hosting rewrite issue; the Angular dev server already handles it, production hosts need a fallback toindex.html.
Next in the series
Continue with the accessible contact form (Tailwind and template-driven forms), then move its submission state into NgRx, and finally connect a product search to a Node and Express API.
Leave A Comment
You must be logged in to post a comment.