Skip to content
OVEX TECH
Education & E-Learning

Build a Dynamic Shortest Path Finder with CSS Only

Build a Dynamic Shortest Path Finder with CSS Only

Mastering Advanced CSS: Calculating Shortest Paths

In this tutorial, we’ll dive deep into a fascinating CSS-only demonstration that calculates and highlights the shortest path between multiple points. While the original example uses JavaScript for drag-and-drop functionality, we’ll focus on the core CSS techniques that make this dynamic visualization possible. You’ll learn how to leverage CSS counters, custom properties, trigonometric functions (like hypotenuse), view timelines, and style queries to achieve complex calculations and visual feedback without relying on JavaScript for the core logic.

Prerequisites

  • Basic understanding of HTML and CSS.
  • Familiarity with CSS custom properties (variables).
  • Knowledge of CSS pseudo-elements (::before, ::after).
  • Access to a modern browser that supports CSS View Timelines and Container Style Queries (e.g., Chrome).

Understanding the Core Concept

The demonstration involves several points (A, B, C, D, E) and a starting point (S). The goal is to calculate the total distance of different paths from S to E (e.g., S-A-C-E, S-A-D-E) and then identify and visually emphasize the path with the shortest total distance. This is achieved by using CSS to:

  • Store and retrieve positional data using custom properties.
  • Calculate distances, including using the hypotenuse function for straight-line distances.
  • Use CSS counters to display calculated distances.
  • Employ view timelines to track element positions dynamically.
  • Utilize container style queries to compare calculated distances and determine the minimum.
  • Visually indicate the shortest path through changes in opacity or other styles.

Step-by-Step CSS Implementation

1. Setting Up Positioning and Custom Properties

Each point (A, B, C, D, E) is positioned using simple top and left properties. Crucially, their positions are also stored in custom properties (e.g., --x and --y). These custom properties are essential for performing calculations.

The JavaScript in the original demo is solely for the drag-and-drop interaction. To understand the CSS, you can comment out the JavaScript. The positions themselves are retrieved using the attr() function, which allows CSS to access HTML attributes. For example, you might see something like:

--x: attr(data-x); --y: attr(data-y);

Where data-x and data-y are attributes on the HTML elements representing the points.

2. Calculating Distances with Trigonometry

To find the distance between two points (x1, y1) and (x2, y2), we can use the distance formula, which is derived from the Pythagorean theorem: distance = sqrt((x2 - x1)^2 + (y2 - y1)^2).

CSS provides the hypot() function, which directly calculates the square root of the sum of squares of its arguments. This is perfect for finding the straight-line distance between two points. For instance, to find the distance between point S and point A, you might use:

--sa-distance: hypot(var(--s-x) - var(--a-x), var(--s-y) - var(--a-y));

These individual distances are then summed up to represent the total length of a path (e.g., S-A-C-E).

Expert Note: The round() function is used in some calculations. This is primarily to prevent potential sub-pixel rendering issues or to ensure cleaner numerical outputs, especially when dealing with values derived from user interactions or complex calculations.

3. Utilizing CSS Counters for Display

CSS counters are used to display the calculated distances. A counter is initialized and then updated to show the computed values. The counter-reset property initializes a counter, and counter-set or content property with counter() can be used to display its value.

In this example, counters named ‘d’ (likely for distance) are used. The calc() function is often employed within counter calculations, combining various custom property values.

.element { counter-reset: d 0; /* Initialize counter */ } 
.element::after { content: counter(d); /* Display counter value */ }

The content: counter(d); combined with counter-set allows the displayed number to update dynamically.

4. Dynamic Positioning with View Timelines

View timelines (a feature related to scroll-driven animations) are key to making the positions trackable. They allow animations or style changes to be linked to the element’s position within the viewport or a scroll container.

Custom properties representing the element’s position (e.g., --sax for S to A on the X-axis) are updated based on the view timeline. This means as you move elements (if using JavaScript for interaction), the CSS can react to these changes because the view timeline is tracking their position.

animation: slide-in linear forwards; 
animation-timeline: --custom-timeline; /* Example */

The --sax and --say custom properties are likely updated by the view timeline, reflecting the current position of elements along the X and Y axes relative to the viewport.

5. Comparing Distances with Style Queries

This is where the magic of finding the shortest path happens. Container style queries (specifically, range-based queries) are used to compare the calculated total distances of different paths.

For each path (e.g., S-A-C-E, S-A-D-E), the total distance is calculated and stored in a custom property. Then, a style query checks if this path’s total distance is the minimum among all possible paths.

.path-sac { 
  --total-distance-sac: calc(var(--sa-distance) + var(--ac-distance) + var(--ce-distance)); 
}

.path-sac:style([--total-distance-sac = min(--all-path-distances)]) { 
  /* Styles to highlight this path */ 
  opacity: 1; 
}

The min() function can be used within CSS to find the smallest value among several custom properties. The style query then checks if a specific path’s distance equals this minimum value.

Expert Note: The sign() function is used for opacity calculations. This function returns -1, 0, or 1 based on the sign of its argument. In this context, it’s likely used to control the visibility or emphasis of elements based on whether they are part of the shortest path, possibly by mapping the output of sign() to opacity values.

6. Highlighting the Shortest Path

When a path’s total distance is identified as the minimum via the style query, its corresponding element (e.g., the line representing the path) receives specific styles. This typically involves changing its opacity to make it more prominent than other paths.

The content property can also be updated to display which path is currently the shortest (e.g., ‘SAC’ or ‘SAD’). This is achieved by setting the content of a pseudo-element based on the style query’s condition.

.path-sac::after { 
  content: 'SAC'; 
} 

.path-sac:style([--total-distance-sac = min(...)]) { 
  /* Change content or other styles */ 
  opacity: 1; 
  font-weight: bold; 
}

7. Understanding Gradients and Visuals

The demonstration also uses conic-gradient for visual effects. While it might appear complex, it’s often used to create thin lines or highlight areas without resorting to borders on the entire element. By carefully controlling the background-size and positioning of the gradient, you can make it look like a thin bar or indicator.

border properties are also used, and by inspecting the element’s bounding box with a border, you can understand how the background gradients are applied within that space.

Conclusion

This CSS-only shortest path finder is a testament to the rapidly expanding capabilities of modern CSS. By combining custom properties, trigonometric functions, counters, view timelines, and style queries, developers can now achieve complex calculations and dynamic behaviors that were once exclusively in the domain of JavaScript. While recreating this specific example might require significant effort and deep understanding, it serves as an excellent illustration of what’s possible and pushes the boundaries of declarative styling.


Source: CSS is too powerful now (YouTube)

Leave a Reply

Your email address will not be published. Required fields are marked *

Written by

John Digweed

1,380 articles

Life-long learner.