HTML and CSS : ADVANCED LEVEL

Last updated on

Advanced HTML (Rich Data, Accessibility & APIs)

At this level, you move beyond static documents. You start working with browser APIs, providing rich data for machines, and building accessible, dynamic widgets that feel like native applications.

1. Rich Structured Data (JSON-LD)

Your intermediate doc covered meta tags for basic SEO and social sharing (Open Graph). The advanced level is Structured Data, which tells search engines exactly what your content is about (a recipe, an event, a product, an article) so it can be shown in rich results.

Code Snippet: (This goes in your <head>)

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Mr. Whiskers' Deep Thoughts on Cat Philosophy",
  "author": {
    "@type": "Person",
    "name": "Mohd. Owais"
  },
  "datePublished": "2025-10-27",
  "image": "https://techwithowais.co.in/images/mr-whiskers-og.jpg",
  "articleBody": "Today, Mr. Whiskers explored the existential void between his morning nap and his afternoon snack..."
}
</script>

Conceptual Explanations:

  • type="application/ld+json": This tells the browser this <script> tag contains JSON-LD (JavaScript Object Notation for Linked Data), not executable JavaScript.
  • @context & @type: This is the “dictionary.” You’re telling Google, “I’m using the vocabulary from schema.org, and the specific ‘thing’ I’m describing is a BlogPosting.”
  • Why it’s advanced: This is how you power “Rich Snippets” in Google—think recipe cards, review stars, and event times right in the search results. You are no longer just describing your page; you are providing machine-readable data.

2. The <dialog> Element

You used to need complex JavaScript and CSS to create a modal (a pop-up window). HTML now gives us a native, accessible, and powerful element for this.

Code Snippet:

<button id="open-modal">Show Modal</button>

<dialog id="my-modal">
  <form method="dialog">
    <h3>This is a modal!</h3>
    <p>It's native HTML. It traps focus and is accessible by default.</p>
    <button type="submit">Close</button>
  </form>
</dialog>

<script>
  const modal = document.getElementById('my-modal');
  const openBtn = document.getElementById('open-modal');
  
  openBtn.addEventListener('click', () => {
    modal.showModal(); // This is the magic browser API
  });
</script>

Conceptual Explanations:

  • <dialog>: This element is hidden by default.
  • modal.showModal(): This JavaScript API call does three crucial things:
    1. Displays the dialog.
    2. Puts it in a “top layer,” above all other content (even content with z-index: 9999).
    3. Traps focus: Pressing “Tab” will only cycle between focusable elements inside the modal, which is a critical accessibility win.
  • method="dialog": This form attribute makes any button inside it close the dialog, no extra JS needed.

3. Advanced Accessibility (ARIA Widgets)

Intermediate HTML focused on semantic accessibility (like label). Advanced accessibility is about using ARIA (Accessible Rich Internet Applications) roles to build complex, dynamic widgets that don’t have native HTML tags (like a custom tab group).

Code Snippet: (This requires JavaScript to function)

<div class="tabs">
  <div role="tablist" aria-label="Example Tabs">
    <button role="tab" aria-selected="true" id="tab-1" aria-controls="panel-1">
      Tab 1
    </button>
    <button role="tab" aria-selected="false" id="tab-2" aria-controls="panel-2" tabindex="-1">
      Tab 2
    </button>
  </div>
  
  <div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
    <p>Content for Tab 1...</p>
  </div>
  <div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
    <p>Content for Tab 2...</p>
  </div>
</div>

Conceptual Explanations:

  • role="tablist": Tells a screen reader, “This is a container for tab buttons.”
  • role="tab": Identifies a button as a tab control.
  • aria-selected="true/false": The most important part. It tells the screen reader which tab is currently active. (You must toggle this with JavaScript).
  • aria-controls="panel-1": Links the button to the panel it controls.
  • role="tabpanel": Identifies a container as a content panel.
  • aria-labelledby="tab-1": Links the panel back to its controlling button.
  • hidden: This HTML attribute hides the inactive panel visually and from screen readers.

4. Advanced Media (<video> & Inline <svg>)

You’ve mastered <img>. Now, let’s look at media you can control and style.

Code Snippet:

<video controls preload="metadata" poster="mr-whiskers-poster.jpg">
  <source src="mr-whiskers.mp4" type="video/mp4">
  <source src="mr-whiskers.webm" type="video/webm">
  <track
    label="English"
    kind="subtitles"
    srclang="en"
    src="subtitles-en.vtt"
    default>
  <p>Your browser does not support the video tag.</p>
</video>

<svg 
  xmlns="http://www.w3.org/2000/svg" 
  viewBox="0 0 100 100" 
  class="icon-heart"
  aria-label="Heart icon"
  role="img"
>
  <path d="M 50,90 C 10,60 40,30 50,50 C 60,30 90,60 50,90 Z" />
</svg>
/* You can style inline SVG elements just like HTML! */
.icon-heart {
  width: 50px;
  fill: grey;
  transition: all 0.3s ease;
}
.icon-heart:hover {
  fill: red;
  transform: scale(1.1);
}

Conceptual Explanations:

  • Multiple <source>s: You provide both MP4 and WebM files. The browser will pick the first one it supports.
  • preload="metadata": A performance trick. It tells the browser to only load the video’s metadata (like duration, dimensions) on page load, not the whole video file.
  • <track>: This is for accessibility, providing captions, subtitles, or descriptions.
  • Inline <svg>: By pasting the SVG code directly into your HTML (instead of <img>), you get two superpowers:
    1. It’s part of the DOM, so CSS can style its individual path, circle, etc. (e.g., change the fill color on hover).
    2. It saves an HTTP request, which is faster.

Advanced CSS (Dynamics, Selectors & Modern Layout)

This level is about writing CSS that is smarter, more dynamic, and less brittle. You’ll stop thinking about “pages” and start thinking about “components.”

1. Advanced Selectors (:has(), :is(), Attribute)

For decades, CSS could only select “down” (parent > child). The new :has() selector lets you select “up” or “sideways,” changing the parent based on its children.

Code Snippet:

<div class="card">
  <h4>A Card Title</h4>
  <p>Some text content...</p>
</div>

<div class="card card--has-image">
  <img src="img.jpg" alt="example">
  <h4>A Card with an Image</h4>
  <p>This one has a different layout.</p>
</div>

<div class="form-group">
  <label for="email">Email</label>
  <input type="email" id="email" required>
</div>
/* 1. The :has() "Parent" Selector */
.card:has(img) {
  /* Selects any .card that has an <img> inside it */
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* 2. The Attribute Selector */
.form-group:has(input:required) label::after {
  /* Selects the label IN a group that has a required input */
  content: " *";
  color: red;
}

/* 3. The :is() "Matcher" Selector */
/* OLD WAY */
.article header h1,
.article header h2,
.article header p {
  color: #333;
}

/* NEW WAY with :is() */
.article header :is(h1, h2, p) {
  /* Selects h1, h2, OR p that is a descendant of header */
  color: #333;
}

Conceptual Explanations:

  • :has(img): This is revolutionary. You’re styling the .card based on its contents. This replaces the need for modifier classes like .card--has-image.
  • Attribute Selectors (:required): You can select elements based on their HTML attributes. input:required is common, but you can use [data-state="active"] to style elements based on JavaScript-driven states.
  • :is(...): This is a powerful “DRY” (Don’t Repeat Yourself) selector. It takes a list of selectors and matches any of them, saving you from writing long, repetitive selector chains.

2. Keyframe Animations (@keyframes)

Your intermediate doc covered transition, which is a simple A-to-B change. @keyframes are for multi-step, complex, and looping animations.

Code Snippet:

/* 1. Define the "story" of the animation */
@keyframes fadeInAndUp {
  0% {
    opacity: 0;
    transform: translateY(20px);
  }
  100% {
    opacity: 1;
    transform: translateY(0);
  }
}

/* 2. Apply the animation to an element */
.my-element {
  animation-name: fadeInAndUp;
  animation-duration: 0.5s;
  animation-timing-function: ease-out;
  animation-fill-mode: backwards; /* Ensures element starts at 0% state */
}

Conceptual Explanations:

  • @keyframes: This is where you define the “stops” (from 0% to 100%) of your animation. You can have many stops (e.g., 0%, 25%, 70%, 100%).
  • animation-name: This links the element to the @keyframes definition.
  • animation-duration: How long the entire animation (from 0% to 100%) takes.
  • animation-fill-mode: backwards: This is an advanced property. It tells the element to apply the styles from the 0% keyframe before the animation even starts. This prevents the element from “flickering” into view before fading in.

3. Advanced Grid: Intrinsic Layouts (minmax, auto-fit)

Your intermediate doc used grid-template-areas to “draw” a specific, static layout. Advanced Grid is about creating intrinsic, responsive layouts that automatically adjust to the content and screen size, with no media queries.

Code Snippet: (A responsive card grid)

<div class="card-grid">
  <div class="card">...</div>
  <div class="card">...</div>
  <div class="card">...</div>
  <div class="card">...</div>
</div>
.card-grid {
  display: grid;
  /* This is the magic line */
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: 1rem;
}

Conceptual Explanations:

  • repeat(...): A function to create repeating tracks.
  • auto-fit: This keyword tells the grid: “Create as many columns as will fit in the available space.”
  • minmax(300px, 1fr): This is the rule for each of those columns. It says:
    • “Each column must be at least 300px wide.”
    • “If there’s extra space, stretch them up to a maximum of 1fr (1 share of the free space).”
  • The Result: On a wide screen, you’ll get 4 columns. On a tablet, it might automatically drop to 2 columns. On a phone, it will automatically become 1 column. You’ve created a fully responsive grid with one line of CSS.

4. Graphical Effects (filter, backdrop-filter, clip-path)

This is how you break out of the “box” and create modern, graphical designs.

Code Snippet:

/* 1. "Glassmorphism" Effect */
.glass-panel {
  background: rgba(255, 255, 255, 0.2);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.3);
}

/* 2. Grayscale-to-Color Image */
.portfolio-item img {
  filter: grayscale(100%);
  transition: filter 0.3s ease;
}
.portfolio-item:hover img {
  filter: grayscale(0%);
}

/* 3. Non-Rectangular Shapes */
.hexagon {
  clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%);
}

Conceptual Explanations:

  • backdrop-filter: blur(10px): This is the key to “frosted glass.” It applies a blur() to whatever is behind the element.
  • filter: grayscale(100%): This applies a graphical effect (like Photoshop) to the element itself. You can also use blur(), brightness(), saturate(), etc.
  • clip-path: polygon(...): This “cuts out” the element into a shape. The polygon() function takes a list of X/Y coordinates to draw a custom shape. This is how you escape the rectangle.

5. Container Queries (@container)

Media queries (@media) let you change styles based on the viewport (the page). Container queries (@container) are the new, advanced way: they let you change styles based on the element’s parent container. This is the key to “component-based” design.

Code Snippet:

<div class="container-wrapper"> <main>
    <div class="my-component">...</div> </main>
  <aside>
    <div class="my-component">...</div> </aside>
</div>
/* 1. Define an element as a "query container" */
main,
aside {
  container-type: inline-size;
  container-name: card-container;
}

/* 2. The component's default styles */
.my-component {
  display: grid;
  grid-template-columns: 1fr;
}

/* 3. The Query: "When this component lives inside a
      container named 'card-container' that is 
      wider than 400px, change its layout" */
@container card-container (min-width: 400px) {
  .my-component {
    grid-template-columns: 100px 1fr; /* Go to 2-column layout */
  }
}

Conceptual Explanations:

  • container-type: inline-size: This “turns on” container queries for an element, telling the browser to “watch” its width.
  • @container (min-width: 400px): This is the query. It works just like a media query, but it’s based on the parent container’s width, not the whole page.
  • Why it’s advanced: The .my-component in <main> (which is wide) might be 2 columns, while the exact same component in <aside> (which is narrow) will automatically be 1 column. The component adapts to its context, not the page. This is the future of modular CSS.