Free Programming learning guide
Master Tailwind CSS for Web Design
Master Tailwind CSS for Web Design — a free intermediate-level guide covering learn tailwind css for web design. Learn with clear explanations, real...
What you will learn
- Environment Setup & Configuration
- Advanced Utility Patterns & Design Tokens
- Responsive Design & Custom Breakpoints
- State Variants & Interactive Selectors
- Complex Layouts with Flexbox & Grid
- Component Extraction & @apply Directive
- Transitions, Transforms, & Animations
- Arbitrary Values, Plugins, & Extending the Core
- Production Optimization & Performance
- Real-World UI Architecture
1. Environment Setup & Configuration
You’ve just built a beautiful, responsive product landing page using utility classes. On your local machine, it renders flawlessly. But when you push the code to production, the CSS file balloons to 4 megabytes, the site’s performance score plummets, and the LCP (Largest Contentful Paint) creeps toward a sluggish 5 seconds. This isn't a bug in your HTML; it’s a configuration failure. Tailwind CSS is a utility-first engine, but without a strict environment setup, it generates every conceivable utility class by default. The framework's true power—delivering a microscopic CSS footprint in production—relies entirely on how you configure your build process and content paths. Getting Tailwind running in a modern web project requires more than just dropping a <link tag into an HTML file. To leverage the full ecosystem, including custom theming, future-proof syntax, and tree-shaking, you need to integrate Tailwind into your JavaScript build pipeline. Choosing Your Integration Strategy Modern Tailwind offers two primary methods for integrating into a project: the Tailwind CLI and the PostCSS Plugin. As an intermediate developer, you will likely encounter both, so understanding when to use each is critical. - Tailwind CLI: A standalone executable. It is incredibly fast, requires zero configuration files (other than tailwind.config.js), and is perfect for static sites, simple projects, or frameworks that don't rely heavily on a traditional PostCSS pipeline (like older Ruby on Rails or static HTML generators). - PostCSS Plugin: The traditional integration method. It weaves Tailwind into your existing CSS processing pipeline. This is the go-to choice for modern bundlers like Vite and Webpack, especially when you need to combine Tailwind with other PostCSS plugins (like autoprefixer). A third option—the Tailwind Play CDN—exists for rapid prototyping, but it should never be used in a production environment. It downloads the entire framework at runtime, entirely defeating Tailwind’s performance benefits. Initializing the Project and Installing Tailwind For this setup, we will target a modern Vite environment, utilizing the PostCSS plugin method. Vite is the current industry standard for front-end tooling due to its near-instant cold starts and lightning-fast Hot Module Replacement (HMR). If you are using Webpack, the PostCSS configuration steps will be identical, though your package.json scripts will differ. First, scaffold a new Vite project and install the required Tailwind packages. Running npx tailwindcss init creates a tailwind.config.js file in your project root. If you prefer TypeScript or JSON, you can run npx tailwindcss init -p (which also generates a postcss.config.js file) or npx tailwindcss init --ts. Configuring PostCSS Because we are using Vite, we need to tell the bundler to process our CSS through Tailwind. Create a postcss.config.js file in your project root if it wasn't generated automatically: Vite automatically picks up this file and applies it …
2. Advanced Utility Patterns & Design Tokens
The Architecture of Design Tokens A design system without constraints is just a suggestion. When a team hands a mockup to a developer, the implicit expectation is pixel-perfect fidelity. Yet, without a shared vocabulary for spacing, typography, and color, the translation from Figma to code inevitably drifts. Tailwind CSS solves this not by providing unlimited utilities, but by providing a strictly bounded set of them. At the heart of Tailwind lies its design token architecture. These tokens—defined in your tailwind.config.js file—are the single source of truth for your project's visual language. As we established in the Environment Setup & Configuration chapter, modifying these tokens should primarily be done using theme.extend: to layer your customizations on top of the defaults, rather than using theme: to overwrite them completely. Understanding how to manipulate these tokens moves you from simply using Tailwind to actively directing it. Let’s look at how to harness the three core pillars of design tokens: typography, color, and spacing. Manipulating Typography Scales Typography is rarely just about picking a font. It involves establishing a harmonious scale, controlling line heights, and setting letter spacing. Tailwind handles the complex math of typography scales through interconnected utility classes. Font Sizes and Line Heights Tailwind’s default text-{size} utilities are paired with sensible default line heights. For example, applying text-xl automatically sets the font size to 1.25rem and the line height to 1.75rem. However, as interfaces grow more complex, you’ll often need to decouple these. You can override the default line height using the leading-{size} utilities. Letter Spacing and Font Weight Tracking (letter spacing) should generally decrease as font size increases. Tailwind defaults to this logic. A large hero headline (text-5xl) looks better with tighter tracking (tracking-tight), while small, uppercase eyebrows (text-xs uppercase) require wider tracking (tracking-widest). When customizing your typography tokens in theme.extend:, you can introduce your own font families and link them to specific weights: Applying Color Palettes & Opacity Modifiers Color application in Tailwind is built on a robust, scale-based system. Instead of naming colors arbitrarily (e.g., primary-blue), Tailwind uses a numeric scale from 50 to 900 (and beyond to 950). This forces consistency. You no longer have to guess if a button should be blue or blue-dark—it’s blue-600 for default states, blue-700 for hover, and blue-500 for active. The Opacity Modifier Syntax One of Tailwind’s most powerful features for color application is the opacity modifier. Instead of needing separate utility classes or RGBA values for transparent colors, you can append an opacity percentage directly to the color utility using a slash (/). Under the hood, Tailwind generates this using modern CSS rgb() space-separated syntax with an alpha channel (e.g., rgb(99 102 241 / 0.5)). This means you can dynamically adjust …
3. Responsive Design & Custom Breakpoints
The Mobile-First Imperative A desktop navigation bar proudly displaying six links, a search bar, and a user avatar looks pristine on a 27-inch monitor. Shrink that viewport down to 375 pixels, and you have a chaotic pile of wrapped text and broken layouts. Historically, tackling this required writing complex CSS media queries that targeted specific device dimensions. In Tailwind CSS, responsive design is handled at the utility level using mobile-first breakpoint prefixes. Instead of writing base styles for desktop and scaling down, you write base styles for mobile and scale up. Because you already understand how to configure your environment and manipulate design tokens, we can dive straight into how Tailwind compiles these prefixes, how to configure them, and how to utilize max-width variants for layouts that defy the standard mobile-first approach. How Tailwind's Breakpoint System Works Tailwind generates its responsive styles using min-width media queries. When you apply a utility class without a prefix, it applies to all screen sizes. When you add a breakpoint prefix—like md:—you are instructing the browser to apply that style only when the viewport is at least that specific width. Here is Tailwind’s default breakpoint scale: sm: 640px md: 768px lg: 1024px xl: 1280px 2xl: 1536px The Cascade and Override Logic Because Tailwind is mobile-first, the cascade works upwards. If you want a element to be full-width on mobile, half-width on tablets, and a quarter-width on large screens, you structure your classes from smallest to largest: The browser reads w-full. At 768px, md:w-1/2 overrides it. At 1024px, lg:w-1/4 takes over. A common pitfall for intermediate developers is forgetting the cascade. If you write class="md:w-1/2 w-full", the w-full will override the md:w-1/2 because they share the same specificity and w-full appears later in the compiled CSS stylesheet. Always order your classes from smallest to largest viewport in your markup to keep the mental model consistent with the compiled output. Configuring Custom Breakpoints The default scale is built around standard device viewports, but modern web design often demands unique constraints. Perhaps you are building an embedded dashboard application that lives in a narrow sidebar, or your design team has specific breakpoints dictated by a Figma file that don't align with Tailwind's defaults. In Chapter 1, we established the golden rule: Extend, Don't Replace. However, breakpoints are an exception to this rule. To alter your breakpoints, you must use the theme: property (not theme.extend:) to completely override the screens key in your tailwind.config.js. Replacing the Default Screens If you use theme.extend.screens, Tailwind will simply append your custom breakpoints to the existing ones. To redefine the scale entirely, use theme.screens: Now, sm:, md:, and lg: no longer exist. Your utilities will be prefixed with tablet:, laptop:, and desktop:. …
4. State Variants & Interactive Selectors
The Mechanics of State Variants A static web page is rarely the end goal. Users hover over links, tab through inputs, click buttons, and select text. In traditional CSS, styling these states requires writing pseudo-classes like :hover, :focus, or :active. Tailwind CSS abstracts these pseudo-classes into state variants—prefixes added to your utility classes that dictate exactly when a style should apply. The syntax is straightforward: prefix the utility with the state name, followed by a colon. When the Tailwind compiler processes this, it generates the corresponding CSS: Because you already understand how to configure colors and spacing using theme.extend from our work with design tokens, applying them conditionally via variants is simply a matter of adding the appropriate prefix. Common Interaction States Tailwind provides variants for almost every native CSS pseudo-class. The most frequently used for interactive elements include: - hover: - Triggers when the cursor is over the element. - focus: - Triggers when the element (like an input or button) receives focus, typically via tabbing or clicking. - active: - Triggers during the brief moment an element is being activated (e.g., while a mouse button is held down on it). - focus-visible: - Triggers only when the element receives focus via keyboard navigation, not mouse clicks. This is crucial for accessibility, allowing you to remove outline rings for mouse users while keeping them for keyboard users. - disabled: - Triggers when the disabled attribute is present on the element, allowing you to gray out buttons or inputs. Stacking and Combining Variants Just as you can combine responsive prefixes with utilities, you can stack state variants with responsive variants. The order matters: the responsive prefix always comes first, followed by the state, followed by the utility. You can also stack multiple states. If you need a button to change its text color only when it is focused and hovered, you can chain the prefixes: Relational Styling with Group and Peer Styling an element based on its own state is simple. But what happens when you need to style an element based on the state of a different element? In traditional CSS, you might use the sibling selector (~ or +) or the descendant selector combined with a pseudo-class (e.g., .parent:hover .child). Tailwind provides a structured way to handle this: group for parent-child relationships and peer for sibling relationships. The group Variant The group variant is used when you want to style child elements based on the state of a parent container. To use it, you must first designate the parent element by adding the group class. Then, on any child element, you can use the group-hover:, group-focus:, or group-active: prefixes. Real-World Example: Product Card Reveal Imagine an e-commerce product …
5. Complex Layouts with Flexbox & Grid
The Layout Engine: Flexbox vs. Grid A web page isn't just a series of stacked divs; it's an architectural blueprint. While standard flow handles linear document structures beautifully, modern web applications demand complex, multidimensional arrangements. Tailwind CSS provides two distinct engines for this: Flexbox for one-dimensional flows and CSS Grid for two-dimensional matrices. Because you already understand the core mechanics of Tailwind from our work in Advanced Utility Patterns & Design Tokens and Responsive Design & Custom Breakpoints, we will bypass the basics of flex and grid and dive straight into architectural application. The goal here is to move beyond simple rows and columns, using Tailwind's utilities to construct resilient navigation systems, asymmetric dashboards, and arbitrary layouts that previously required custom CSS. Advanced Flexbox: Navigation and Card Components Flexbox excels at distributing space along a single axis, making it the default choice for UI components like navigation bars, toolbars, and card footers. However, intermediate layouts often require managing dynamic content sizes, preventing overflow, and aligning nested elements precisely. Building a Resilient Navigation Bar A common pitfall in navigation design is handling dynamic content—like varying user email lengths or unexpected notification badges—without breaking the layout. By combining Flexbox utilities with min-width and flex-shrink behaviors, we can build a navigation bar that absorbs space intelligently. In this layout, the flex-1 utility on the search container tells it to grow and consume all available space, while max-w-xl ensures it doesn't overwhelm the design on ultra-wide screens. The shrink-0 utility on the brand and actions sections guarantees they retain their dimensions even if the search input requires substantial space. The Asymmetric Card Component Card layouts often struggle with alignment when content lengths vary. A product card might have a short title but a long description, pushing the "Add to Cart" button out of alignment. Flexbox solves this with column-based alignment and the mt-auto (margin-top: auto) trick. By setting the card to flex flex-col and applying flex-1 to the content wrapper, we create an elastic container. The mt-6 on the pricing section, combined with the flex context, ensures that no matter how short the description is, the price and call-to-action button are always pinned to the bottom of the card. This is essential when displaying grids of cards with varying text lengths. CSS Grid: Two-Dimensional Layouts While Flexbox handles one-dimensional flows perfectly, CSS Grid allows you to manage rows and columns simultaneously. This is critical for page-level layouts like dashboards and application shells. Implicit vs. Explicit Grids Tailwind's grid-cols- utilities define explicit column tracks, but the rows are implicit—they are created as needed based on the content. For complex layouts, you often need to define both. Consider a standard analytics dashboard. You want a sidebar, …
6. Component Extraction & @apply Directive
The Tipping Point of Utility Proliferation You’re building a SaaS dashboard. You’ve just finished styling the primary call-to-action button using the utility patterns, state variants, and design tokens covered in previous chapters. It looks fantastic. The markup looks like this: Now, you need to add this exact button to the settings page, the user profile page, and a modal. You copy and paste the class string. A week later, your design team decides all primary buttons need a slightly larger font and more horizontal padding. You open your IDE, initiate a global search for that specific 12-class string, and manually replace it across 14 different files. This is the tipping point of utility proliferation. While utility-first CSS shines for rapid, unopinionated UI development, repeating identical, complex utility strings across a codebase creates a massive maintenance liability. Tailwind solves this not by abandoning utilities, but by offering a bridge back to traditional component classes via the @apply directive. The @apply Directive: Bridging HTML and CSS The @apply directive allows you to extract multiple Tailwind utility classes into a single custom CSS class. Instead of writing raw CSS properties (like padding: 0.5rem), you use @apply to inline Tailwind’s utilities directly within your global stylesheet. When Tailwind’s PostCSS plugin processes your CSS, it finds @apply, resolves the corresponding utility classes, and replaces the directive with the actual CSS rules. Where @apply Lives If you recall from The Three Directives introduced during Environment Setup, your main CSS file is structured in three layers: Classes created with @apply belong in the components layer. You define them using the @layer components directive. Placing them here ensures two critical things: 1. Specificity management: Component classes sit between base styles and utilities. If you need to override a component class on a specific element, you can simply apply a utility class directly in your HTML, and the utility will win. 2. Proper purging: Tailwind’s content scanner will correctly identify and keep the custom classes you define, avoiding the Common Purging Pitfalls associated with dynamically generated class names. Here is how you extract that dashboard button: Now, your HTML across the entire application becomes wonderfully terse: If the design team requests changes, you update the .btn-primary class in one place, and the changes cascade across the entire application. Component Extraction vs. Utility Composition A common dilemma developers face is deciding when to extract a component class and when to simply compose utilities directly in the HTML (often using a frontend framework's component model). Both approaches have distinct trade-offs. When to Extract with @apply Extraction is most beneficial when dealing with highly repetitive, opinionated design patterns that require strict visual consistency across disparate parts of an application. Use component extraction …
7. Transitions, Transforms, & Animations
The Mechanics of Motion A user hovers over a button, and it instantly changes color. It works, but it feels jarring—like a stage actor breaking character. The web is an interactive medium, and sudden state changes break the illusion of a tangible, physical interface. Motion bridges that gap. It provides context, indicates causality, and guides the user’s attention. In Tailwind CSS, adding motion is a matter of stacking utility classes. Because you already understand how to apply state variants and manage your design tokens, implementing motion comes down to learning the specific utility namespaces Tailwind uses to map to CSS transitions, transforms, and keyframe animations. Transitions: Smoothing State Changes Transitions allow property changes to occur smoothly over a given duration rather than instantly. In raw CSS, this requires defining transition-property, transition-duration, transition-timing-function, and transition-delay. Tailwind provides dedicated utilities for each. Defining What to Animate By default, Tailwind’s transition utility applies a transition to three common properties: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, and filter. This covers the vast majority of interactive UI needs without adding unnecessary overhead. If you need to transition a specific property outside the defaults, or restrict the transition to just one property, you can use targeted utilities: - transition-colors: Limits the transition to color-related properties. - transition-opacity: Limits to opacity. - transition-shadow: Limits to box-shadow. - transition-transform: Limits to transform properties. - transition-none: Disables transitions entirely. Duration, Easing, and Delay A transition isn't complete without timing. Tailwind maps these to your theme configuration, allowing you to maintain consistent motion design tokens across your project. - Durations: Use duration-150, duration-300, duration-700, etc. These map to milliseconds. - Timing Functions (Easing): Tailwind provides ease-linear, ease-in, ease-out, and ease-in-out. As we established in Advanced Utility Patterns & Design Tokens, you can add custom cubic-bezier curves in your tailwind.config.js under theme.extend.transitionTimingFunction to reflect your brand's specific motion language. - Delays: Apply delay-75, delay-150, etc., to stagger animations or ensure a transition occurs slightly after an interaction begins. Real-World Example: The Interactive Pricing Card Consider a pricing card where the border and shadow need to highlight smoothly when hovered, but the internal "Sign Up" button needs a slight delay before scaling up. In this example, the parent div handles the color and shadow transitions over 300ms. The button specifically transitions its transform property. Because the button has a delay-150 class, the scale effect waits 150ms before firing, creating a cascading, layered effect rather than a single robotic state change. Transforms: 2D and 3D Manipulation Transforms allow you to manipulate the position, size, and rotation of an element without disrupting the document flow. This makes them highly performant, as the browser doesn't have to recalculate layout for surrounding elements. …
8. Arbitrary Values, Plugins, & Extending the Core
You’ve meticulously crafted your UI using the design tokens and utility patterns established in your configuration. Your grid layouts are flawless, your state variants are interactive, and your components are cleanly extracted. Then, the designer hands you a Figma frame containing a hero section with a background positioned at exactly 23% vertically, a custom CSS grid-template, and a pop of color pulled straight from a brand campaign that doesn't exist in your tailwind.config.js file. Do you break your design system by writing a custom CSS file, or do you fight to force these one-off requirements into Tailwind’s predefined scale? The answer is neither. Tailwind CSS is not a closed system. It provides an escape hatch for highly specific, one-off styling needs, and a robust JavaScript API for permanently extending its core capabilities. Escaping the Scale with Arbitrary Values While Chapter 2 introduced the concept of design tokens and the importance of a constrained scale (like spacing, colors, and fontSizes), real-world development inevitably requires values that fall outside those constraints. Instead of jumping into your CSS files to write custom classes, Tailwind allows you to generate utilities on the fly using arbitrary value syntax. This syntax uses square brackets [...] to tell Tailwind’s engine, "Generate a utility for this exact value." Custom Properties and Exact Values If you need a specific width, a precise grid column span, or an exact hex color, you can pass the raw value directly into the utility. Because Tailwind generates these utilities on demand, they are automatically available for combination with the responsive breakpoints from Chapter 3 and the state variants from Chapter 4. Need that custom background color to change on hover? Arbitrary Variants Arbitrary values aren't just for CSS properties; you can also create arbitrary variants to target specific DOM states or selectors that Tailwind doesn't support out of the box. If you need to style an element based on a custom data attribute, or target a specific child element using an arbitrary selector, you can use the bracket syntax directly after the variant prefix: Handling CSS Variables When working with CSS variables (custom properties), Tailwind provides a shorthand syntax. Instead of forcing you to write bg-[var(--brand-color)], you can simply wrap the variable name in brackets, omitting the var() wrapper: This generates background-color: var(--brand-color);, keeping your markup clean and readable. Real-World Scenario: A Complex CSS Grid Let’s look at a practical application of arbitrary values. Suppose you are building a highly specific bento-box layout. The default grid utilities are great for symmetric layouts, but this design requires an asymmetric grid template that defines exact track sizes. Instead of writing custom CSS, you can use arbitrary values to define the grid-template-columns and grid-template-rows directly in …
9. Production Optimization & Performance
A standard Tailwind CSS development build contains every utility class in the framework. Uncompressed, this file clocks in at 3.5MB or more. If you deploy this raw file to production, you will devastate your site’s load time and fail Core Web Vitals. The magic of Tailwind lies in its ability to tree-shake this massive stylesheet down to only the classes you actually use—often resulting in a final file size under 10KB. However, this optimization is not automatic. It requires precise configuration, an understanding of how the compiler reads your code, and a strategy for delivering the CSS to the browser efficiently. Configuring the Content Array for Perfect Purging In the earlier chapters on Environment Setup & Configuration and Component Extraction & @apply Directive, we briefly touched on configuring your content paths. Now, we need to look at exactly how the Tailwind engine uses those paths to purge unused CSS. When Tailwind compiles for production, it doesn't actually "remove" unused CSS. Instead, it generates only the CSS it finds matched in your source files. The content array tells the compiler exactly which files to scan. If a class exists in your HTML but isn't in a scanned file, it won't be generated. Writing Bulletproof Path Patterns A common mistake is being too restrictive or too broad with glob patterns. Your configuration must account for every file type that contains class names. Notice the use of the wildcard. This ensures that nested directories are scanned. If you only write ./src/.html, the compiler will miss ./src/about/index.html. Handling External JavaScript and Frameworks If you are using a component library that relies on Tailwind classes (like a custom internal UI kit hosted in nodemodules), you must explicitly include those paths. Tailwind ignores nodemodules by default to prevent massive scan times. The "Safelist" Escape Hatch Sometimes you generate class names dynamically at runtime in a way Tailwind cannot statically detect. We covered Dynamic String Construction as a pitfall earlier, but the proper fix for unavoidable dynamic classes is the safelist property. The safelist option forces the compiler to generate specific classes, regardless of whether they appear in your content files. Use safelist sparingly. Every class added here bypasses the tree-shaking phase and bloats your final CSS bundle. Analyzing and Troubleshooting CSS File Sizes If your final CSS file is unexpectedly large, you have a purging issue. The compiler is generating classes it shouldn't be, which means it is finding strings in your source code that look like Tailwind classes but aren't actually being used as CSS classes. Common Sources of False Positives Tailwind uses a simple extractor to scan files. It looks for strings separated by whitespace or quotes that match class naming patterns. This can …
10. Real-World UI Architecture
The Anatomy of a Dashboard You have built your toolkit. You understand how to configure the engine using theme.extend, command complex grids, manage interactive states, and extract reusable components with @apply. But web development in the wild rarely involves building isolated buttons or detached card layouts. It requires assembling these pieces into cohesive, scalable, and maintainable ecosystems. A modern admin dashboard is the ultimate stress test of your UI architecture. It demands a responsive skeleton that doesn't collapse under the weight of dense data, a navigation system that adapts to both mobile screens and desktop workflows, and data presentation layers that remain legible across varying states. To build this, we will architect a production-ready dashboard layout from scratch. We will leverage your knowledge of Complex Layouts with Flexbox & Grid, State Variants & Interactive Selectors, and Advanced Utility Patterns & Design Tokens to create a system that is both rigid in its structure and flexible in its implementation. Architecting the Responsive Shell Before writing a single line of HTML, we need to define the structural anatomy of our application. A standard dashboard consists of three primary regions: 1. Top Navigation: A fixed header containing global search, user controls, and a dark-mode toggle. 2. Sidebar: A secondary navigation rail for contextual links, collapsible on mobile. 3. Main Content Area: The fluid container housing the data table and metrics. Instead of hacking these together with absolute positioning, we will use CSS Grid to establish a macro-layout. This ensures our regions respect each other's boundaries automatically. Defining the Grid Structure We will create a grid container that spans the full viewport height. On mobile, we only want a single column (the top nav and main content), hiding the sidebar by default. On larger screens, we introduce the sidebar as a distinct column. Notice the grid-rows-[auto1fr] arbitrary value. This tells the grid that the header should take up only the height it needs (auto), while the main content area should consume the remaining viewport height (1fr). By transitioning to lg:grid-cols-[auto1fr], we instruct the layout to split into two columns at the lg breakpoint: the sidebar takes its required width (auto), and the main content fills the rest. Because the main element is constrained by the grid's 1fr row, applying overflow-y-auto to it creates an independently scrolling content region. This is critical for complex data tables—your navigation stays fixed while the user scrolls through rows of data. Implementing Dark-Mode Navigation and Sidebar With our shell established, we need to wire up the navigation. Building on Environment Setup & Configuration, we assume you have configured your darkMode: 'class' strategy in your Tailwind config. The Top Navigation Bar The top nav serves as the global control center. …
Continue learning
- Build a Personal Website with HTML and CSSBuild a Personal Website with HTML and CSS — a free beginner-level guide covering build a personal website with html and css. Learn with clear...
- Mastering React JS for Web DevelopmentMastering React JS for Web Development — a free intermediate-level guide covering learn react js for web development. Learn with clear explanations,...
- Intermediate Python Automation Scripts for BeginnersIntermediate Python Automation Scripts for Beginners — a free intermediate-level guide covering intermediate python automation scripts for beginners....
- Intermediate Python Projects for Portfolio BuildingIntermediate Python Projects for Portfolio Building — a free intermediate-level guide covering intermediate python projects for portfolio building....