Opens an external site in a new window
Mental Health Awareness Month
“Community”
RODNEY LAB
  • Home
  • Plus +
  • Newsletter
  • Links
  • Profile
RODNEY LAB
  • Home
  • Plus +
  • Newsletter
  • Links

Astro Scroll to Anchor: Smooth Scroll to Heading # Astro Scroll to Anchor: Smooth Scroll to Heading #

blurry low resolution placeholder image Astro Scroll to Anchor
  1. Home Rodney Lab Home
  2. Blog Posts Rodney Lab Blog Posts
  3. Astro Astro Blog Posts
<PREVIOUS POST
NEXT POST >
LATEST POST >>

Astro Scroll to Anchor: Smooth Scroll to Heading #

Updated 4 months ago
11 minute read
Gunning Fog Index: 7.7
Content by Rodney
blurry low resolution placeholder image Author Image: Rodney from Rodney Lab
SHARE:

⚓️ What is Astro and What is Scroll to Anchor? #

In this post we will see how to build Astro Scroll to Anchor functionality into your static site. Before we get on to that though, we should take a quick peek at what Astro and scroll to anchor are. Astro is a new static site builder which lets you build fast websites. The secret to its speed is something called partial hydration which means you, as a developer, get more control over when JavaScript on a page loads. You can even ship zero JavaScript when none is needed. Scroll to anchor is a nice feature we have come to expect on websites where a little link icon appears if you hover over a heading. You can click the link to scroll smoothly to that heading, as well as even copy and save it for future reference.

🧱 What we’re Building #

blurry low resolution placeholder image Astro Scroll to Anchor: Image is zoomed in on a paragraph of text with a heading above.  Following the heading text is a link icon.
Astro Scroll to Anchor: What we're building

Having said that Astro's superpower is partial hydration, we're going to build out the scroll functionality with no hydration at all. Meaning no JavaScript is required for our page; we will add the smooth scrolling and link auto show/hide using CSS. Although you can use Astro with Lit, React, Svelte or Vue, we will create a pure Astro component to add the feature. This will make it easier for you to recycle the code for use in your own Astro project using your preferred framework. Have a look at the post introducing Astro, though if you want to know more about partial hydration and Astro’s islands architecture.

If you are new to Astro, consider this a gentle introduction. If, however, you already have some experience with Astro you will see a new and efficient way to add SVG icons to your Astro app. This will let you pick icons from any icon library you want, just by adding a single dependency.

🚀 Getting Started #

The code we will look at can easily be added to an existing project, though if you are new to Astro, just follow along and you can add it to your next project! If you are starting a new project, let’s get the ball rolling in the Terminal:

    
pnpm create astro@latest astro-scroll-to-anchor
cd astro-scroll-to-anchor
pnpm install
pnpm astro telemetry disable
pnpm dev

Use yarn or npm if you prefer those to pnpm. Choose Minimal from the list of templates. We disable telemetry here. Skip that line if you want to keep data collection. The Astro dev server will normally run on port 4321 but if there is already something running there, it will find the next available port. The terminal will then tell you which port it settled for:

blurry low resolution placeholder image Astro Scroll to Anchor: Screenshot show Astro C L I terminal output giving the dev server address as http  localhost 3001
Astro Scroll to Anchor: Terminal

This is a great feature, just make sure you only run one server at a time though! You can run multiple servers, but a couple of times I have spun up a new dev server, while I already had one running in preview mode. Of course, the preview one was open in the browser and I couldn't work out why code changes weren't showing up… a fun way to waste ten minutes!

Anyway, if you are following along, starting from scratch, replace the content in src/pages/index.astro with this:

src/pages/index.astro — click to expand code.
src/pages/index.astro
astro
    
1 ---
2 // frontmatter section - nothing to see here yet
3 ---
4
5 <html lang="en-GB">
6 <head>
7 <meta charset="utf-8" />
8 <meta name="viewport" content="width=device-width" />
9 <title>Astro Scroll to Anchor</title>
10 </head>
11 <body>
12 <main class="container">
13 <div class="wrapper">
14 <h1>
15 <Heading text="Astro Scroll to Anchor" id="astro-scroll-to-anchor" />
16 </h1>
17 <h2>Lorem ipsum" /></h2>
18 <p>
19 Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt
20 ut labore et dolore magna aliqua. Suscipit adipiscing bibendum est ultricies integer quis.
21 Tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed. At erat pellentesque
22 adipiscing commodo elit at imperdiet. Suscipit adipiscing bibendum est ultricies integer
23 quis auctor. Velit sed ullamcorper morbi tincidunt ornare massa eget egestas. Imperdiet
24 sed euismod nisi porta. Non blandit massa enim nec. Etiam dignissim diam quis enim
25 lobortis scelerisque fermentum dui. Suspendisse sed nisi lacus sed viverra tellus in.
26 Metus dictum at tempor commodo ullamcorper a. A scelerisque purus semper eget duis at.
27 Ultrices dui sapien eget mi proin sed libero. Cursus metus aliquam eleifend mi in nulla
28 posuere sollicitudin.
29 </p>
30 <h2>Amet porttitor</h2>
31 <p>
32 Amet porttitor eget dolor morbi. Ullamcorper eget nulla facilisi etiam dignissim diam quis
33 enim. Cras tincidunt lobortis feugiat vivamus at. Eleifend donec pretium vulputate sapien
34 nec sagittis aliquam malesuada bibendum. Curabitur gravida arcu ac tortor dignissim.
35 Scelerisque purus semper eget duis. Amet nulla facilisi morbi tempus iaculis urna id. Et
36 ligula ullamcorper malesuada proin libero. Risus pretium quam vulputate dignissim
37 suspendisse in. Nec dui nunc mattis enim ut tellus elementum. At quis risus sed vulputate
38 odio. Facilisi cras fermentum odio eu feugiat pretium. Lorem ipsum dolor sit amet
39 consectetur. Sit amet massa vitae tortor condimentum lacinia quis. Amet volutpat consequat
40 mauris nunc congue nisi vitae suscipit tellus. Posuere lorem ipsum dolor sit amet
41 consectetur adipiscing elit duis. Ac turpis egestas integer eget aliquet nibh. In nibh
42 mauris cursus mattis.
43 </p>
44 <h2>Blandit turpis</h2>
45 <p>
46 Blandit turpis cursus in hac habitasse platea. Egestas tellus rutrum tellus pellentesque
47 eu. In eu mi bibendum neque. Accumsan in nisl nisi scelerisque eu ultrices vitae auctor.
48 Augue mauris augue neque gravida. Tristique nulla aliquet enim tortor at auctor. A
49 pellentesque sit amet porttitor. Pharetra pharetra massa massa ultricies mi. Fringilla ut
50 morbi tincidunt augue interdum velit euismod in pellentesque. Et leo duis ut diam quam
51 nulla porttitor. Pharetra diam sit amet nisl suscipit. Lorem donec massa sapien faucibus.
52 Tempor orci eu lobortis elementum nibh tellus. Urna porttitor rhoncus dolor purus non enim
53 praesent elementum facilisis. Sed nisi lacus sed viverra tellus in hac habitasse.
54 Fermentum leo vel orci porta non pulvinar neque laoreet suspendisse. Enim facilisis
55 gravida neque convallis a cras. Enim nunc faucibus a pellentesque sit amet porttitor. Cras
56 fermentum odio eu feugiat pretium.
57 </p>
58 <h2>Arcu dui</h2>
59 <p>
60 Arcu dui vivamus arcu felis bibendum ut tristique. Congue eu consequat ac felis donec et
61 odio. Semper feugiat nibh sed pulvinar proin gravida hendrerit. Libero nunc consequat
62 interdum varius sit. At volutpat diam ut venenatis. Euismod quis viverra nibh cras.
63 Vestibulum morbi blandit cursus risus. Risus viverra adipiscing at in tellus integer
64 feugiat scelerisque. Tristique senectus et netus et malesuada fames ac. Amet risus nullam
65 eget felis eget nunc lobortis. Nisl pretium fusce id velit ut tortor pretium viverra.
66 Turpis egestas sed tempus urna et pharetra pharetra massa massa. Fermentum dui faucibus in
67 ornare quam viverra orci sagittis. Nam libero justo laoreet sit. Eget velit aliquet
68 sagittis id consectetur purus ut faucibus pulvinar. Nullam ac tortor vitae purus faucibus
69 ornare suspendisse.
70 </p>
71 <h2>Tellus in hac</h2>
72 <p>
73 Tellus in hac habitasse platea dictumst vestibulum rhoncus est pellentesque. Dignissim
74 sodales ut eu sem integer vitae justo. Nunc vel risus commodo viverra. Nunc sed blandit
75 libero volutpat sed cras. Arcu risus quis varius quam quisque id. Tristique sollicitudin
76 nibh sit amet commodo nulla facilisi. Sed vulputate mi sit amet mauris commodo quis
77 imperdiet. Tristique sollicitudin nibh sit amet commodo nulla facilisi. Tellus at urna
78 condimentum mattis. Feugiat scelerisque varius morbi enim. Sit amet aliquam id diam
79 maecenas ultricies mi. Lectus quam id leo in vitae turpis massa sed. Felis donec et odio
80 pellentesque diam volutpat commodo sed egestas. Facilisis gravida neque convallis a cras
81 semper. Velit laoreet id donec ultrices tincidunt. Sed lectus vestibulum mattis
82 ullamcorper velit. Et ultrices neque ornare aenean euismod elementum nisi quis eleifend.
83 </p>
84 </div>
85 </main>
86 </body>
87 </html>
88
89 <style>
90 /* raleway-regular - latin */
91 @font-face {
92 font-family: 'Raleway';
93 font-style: normal;
94 font-weight: 400;
95 src: local(''), url('/fonts/raleway-v26-latin-regular.woff2') format('woff2');
96 }
97
98 @font-face {
99 font-family: 'Raleway';
100 font-style: normal;
101 font-weight: 700;
102 src: local(''), url('/fonts/raleway-v26-latin-700.woff2') format('woff2');
103 }
104
105 @font-face {
106 font-family: 'Raleway';
107 font-style: normal;
108 font-weight: 900;
109 src: local(''), url('/fonts/raleway-v26-latin-900.woff2') format('woff2');
110 }
111
112 :global(html) {
113 --colour-background-hue: 47.36;
114 --colour-background-saturation: 100%;
115 --colour-background-luminance: 52.55%;
116
117 --colour-text-hue: 282.86;
118 --colour-text-saturation: 53.85%;
119 --colour-text-luminance: 35.69%;
120
121 --font-family-heading: Montserrat;
122 --font-family-body: Raleway;
123
124 --font-size-1: 1rem;
125 --font-size-5: 2.441rem;
126 --font-size-6: 3.052rem;
127
128 --font-weight-bold: 700;
129 --font-weight-black: 900;
130
131 --line-height-relaxed: 1.75;
132
133 --max-width-full: 100%;
134 --max-width-wrapper: 38rem;
135
136 --spacing-20: 5rem;
137
138 background-color: hsl(
139 var(--colour-background-hue) var(--colour-background-saturation)
140 var(--colour-background-luminance)
141 );
142 color: hsl(var(--colour-text-hue) var(--colour-text-saturation) var(--colour-text-luminance));
143 }
144
145 :global(h1),
146 :global(h2) {
147 font-family: var(--font-family-heading);
148 }
149
150 :global(h1) {
151 font-size: var(--font-size-6);
152 font-weight: var(--font-weight-black);
153 }
154 :global(h2) {
155 font-size: var(--font-size-5);
156 font-weight: var(--font-weight-bold);
157 }
158
159 :global(p) {
160 font-family: var(--font-family-body);
161 font-size: var(--font-size-1);
162 line-height: var(--line-height-relaxed);
163 }
164
165 .container {
166 display: flex;
167 align-items: center;
168 padding-bottom: var(--spacing-20);
169 }
170
171 .wrapper {
172 width: var(--max-width-full);
173 max-width: var(--max-width-wrapper);
174 margin: 0 auto;
175 }
176 </style>

This is just some placeholder text which will let us explore a few Astro features as we build out the Astro scroll to anchor feature.

Anatomy of an Astro File #

Like Markdown files, Astro files also have a front matter section. This is where you can import packages as well as run any JavaScript you need to for the output. You can also use TypeScript in the front matter.

The next part of the file is essentially a template. You can include JavaScript scripts in script tags, but can't actually run JavaScript code within this section (this is different to the JSX you might use in React, for example). The Astro markup is a superset of HTML meaning making it easy to pick up if you are coming from a HTML/JavaScript only background.

Finally, at the bottom we have some styling. You can include it like this within a script tag in your Astro file. As an alternative, for a typical project, you can create a global CSS stylesheet and import that in your Astro frontmatter. In this case you can still include any styles for the current page in this style tag. If you do want to use global stylesheet, just save it somewhere within the src folder of your project and import it as mentioned.

Final Preparation #

Before proceeding, download some self-hosted fonts which we will use on the site. Save raleway-latin-400-normal.woff2  and raleway-latin-700-normal.woff2  together with raleway-latin-900-normal.woff2  to a new, public/fonts directory within the project.

🧩 Heading Component #

The markup and styling for the Astro scroll to anchor feature will go in a new Astro component file. You can create Astro components as well as pages. The components are akin to those you would have in a React or SvelteKit app. Create a src/components folder and within that directory make a new Heading.astro file, adding this content:

src/components/Heading.astro
astro
    
1 ---
2 import { Icon } from 'astro-icon/components';
3
4 const { 'aria-label': ariaLabel, id, text } = Astro.props;
5
6 const href = `#${id}`;
7 ---
8
9 <span {id} class="container">
10 {text}
11 <a aria-label={ariaLabel} {href}
12 ><span class="anchor-link"><Icon name="heroicons-solid:link" /> </span></a
13 >
14 </span>
15
16 <style lang="css">
17 .anchor-link {
18 visibility: hidden;
19 }
20
21 a {
22 color: hsl(var(--colour-text-hue) var(--colour-text-saturation) var(--colour-text-luminance));
23 text-decoration: none;
24 }
25
26 [astro-icon] {
27 display: inline;
28 width: var(--font-size-5);
29 vertical-align: middle;
30 }
31
32 .container:focus .anchor-link,
33 .container:hover .anchor-link {
34 visibility: visible;
35 }
36 </style>

You see a few Astro features in here. Firstly, like our home page we have three sections: frontmatter, markup and styles. In the first line we import the astro-icon package by Nate Moore , an Astro maintainer. This makes use of Anthony Fu's fantastic icônes library  (used with the iconify package). If you haven’t yet heard of it, it is definitely worth exploring. Go to the icônes site where you can find icons from just about every library that exists. You can pick the icons you want for your app and under the hood, astro-icon efficiently imports just the ones you need.

We use the icon, in line 12. You just select the icon you want on the icônes site and it gives you a name to add which you add as an attribute to the <Icon> component instances. Before that though, we need to install the astro-icon package and the icon pack(s) for your chosen icon(s):

    
pnpm add -D astro-icon @iconify-json/heroicons-solid

and add a few lines of config to astro.config.mjs in the project root folder:

astro.config.mjs
javascript
    
1 import icon from 'astro-icon';
2 import { defineConfig } from 'astro/config';
3
4 // https://astro.build/config
5 export default defineConfig({
6 integrations: [
7 icon({
8 include: {
9 'heroicons-solid': ['link'],
10 },
11 }),
12 ],
13 });

Here, “link” in line 9 is the name of the icon we want to choose from the pack, you can list out all the icons you want to use or, alternatively, use all icons from the pack.

🗳 Poll #

Have you already tried astro-icon, icônes or iconify?
Voting reveals latest results.

Astro props #

In line 4 (of the Header.astro file) you see how to access props in an Astro component, we will include these in the markup for the home page in the next step. The two props will be the text of the title together with an id, used to create the scrolling link. This needs to be unique for each link we create. We actually use the text prop in the markup, in line 10.

Moving in line 11 we use an Astro shortcut (this will look familiar if you know Svelte). We can use this shortcut whenever the name of a variable matches the name of the attribute we want to assign it to:

    
<a aria-label={ariaLabel} {href}>

is equivalent to:

    
<a aria-label={ariaLabel} href={href}>

Finally, in lines 26 – 30, you see we can style the icon by targetting [astro-icon]. Notice the global CSS variables we defined on the home page are available in our component.

🔌 Using the new Component #

Using the new component is a breeze. Let's update src/pages/index.astro first, importing our new Heading component:

src/pages/index.astro
astro
    
1 ---
2 import Heading from '../components/Heading.astro';
3 ---

and then using it in the headings:

src/pages/index.astro
astro
    
13 <div class="wrapper">
14 <h1>
15 <Heading text="Astro Scroll to Anchor" id="astro-scroll-to-anchor" />
16 </h1>
17 <h2><Heading id="lorem-ipsum" text="Lorem ipsum" /></h2>
18 <p>
src/pages/index.astro
astro
    
30 <h2><Heading id="amet-porttitor" text="Amet porttitor" /></h2>
src/pages/index.astro
astro
    
44 <h2><Heading id="blandit-turpis" text="Blandit turpis" /></h2>
src/pages/index.astro
astro
    
58 <h2><Heading id="arcu-dui" text="Arcu dui" /></h2>
src/pages/index.astro
astro
    
71 <h2><Heading id="tellus-in-hac" text="Tellus in hac" /></h2>

Alternative Implementation #

We are passing the text in as a prop. This is so that you have easier access to the title text in the component, so for example, you could add some code to remove widows. This is the pet peeve of typographers where you have a single short word alone on a line. The alternative is to rewrite the component so it accepts the title text sandwiched between the Heading component:

    
<!-- EXAMPLE ONLY WILL NOT WORK WITHOUT UPDATING Heading COMPONENT -->
<h2><Heading id="tellus-in-hac">Tellus in hac</h2>

Then in the heading component, in the markup you would need to replace {text} with <slot/>. We won't go into details, here, just want to let you know another way exists.

If you save and hover over one of the headings, you icon should show up.

🛹 Adding Smooth Scrolling #

The final missing piece is to add a touch of CSS to get smooth scrolling. It might seem counter-intuitive but we will switch off the feature for users who prefer reduced motion. This is just because on a long page, scrolling can be quite quick, and might trigger nausea in site visitors with vestibular disorders.

src/pages/index.astro
astro
    
137 <style>
138 :global(html) {
139
140 /* ...TRUNCATED */
141
142 color: hsl(var(--colour-text-hue) var(--colour-text-saturation) var(--colour-text-luminance));
143
144 scroll-behavior: smooth;
145 }
146 @media (prefers-reduced-motion: reduce) {
147 :global(html) {
148 scroll-behavior: auto;
149 }
150 }

That’s it now! Let’s test it out next.

💯 Astro Scroll to Anchor: Testing it out #

Please enable JavaScript to watch the video 📼

Astro Scroll to Anchor: Test.

🙌🏽 Astro Scroll to Anchor: Wrapping Up #

In this post, we have had an introduction to Astro and seen:

  • how to pass props into an Astro component and access them from within the component;
  • a convenient and efficient way to add SVG icons to your Astro app; and
  • how to make smooth scrolling more accessible.

The full code for the app is available in the Astro demo repo on Rodney Lab GitHub .

I hope you found this article useful and am keen to hear how you plan to use the Astro code in your own projects.

🏁 Astro Scroll to Anchor: Summary #

Do you have to know React or another framework to use Astro? #

Although Astro works with Lit, React, Svelte and Vue, you don’t have to use any of these if your app doesn't need it. Astro, itself a superset of HTML, is a templating language used for markup. When you don’t need JavaScript in your app, using Astro alone will work just fine! This makes Astro a nice choice of framework if you are moving on from plain HTML and JavaScript apps.

How can you easily add SVG icons in Astro? #

astro-icon is a great choice for adding SVG icons to your Astro app. It provides an API for all of the icons in the icônes library. That library includes icons from just about every set of icons you could possibly want to use. astro-icon will efficiently import only the icons you use, helping to keep the shipped bundle size small.

How can you make smooth scrolling more accessible? #

Smooth scrolling looks good on short pages and might not cause accessibility issues. For long pages, the resulting scroll might be quick enough to trigger nausea in sufferers of vestibular disorders. This does not mean that you shouldn’t use the CSS or JavaScript smooth scrolling functions. Instead, just add a media query, setting smooth scrolling to 'auto' if the user has set 'prefers-reduced-motion' to 'reduce' in their browser. Setting 'auto' results in an instant scroll which should provide a better experience for these users.

🙏🏽 Astro Scroll to Anchor: Feedback #

Have you found the post useful? Would you prefer to see posts on another topic instead? Get in touch with ideas for new posts. Also, if you like my writing style, get in touch if I can write some posts for your company site on a consultancy basis. Read on to find ways to get in touch, further below. If you want to support posts similar to this one and can spare a few dollars, euros or pounds, please consider supporting me through Buy me a Coffee.

blurry low resolution placeholder image ask Rodney X (formerly Twitter) avatar

Rodney

@askRodney

Just dropped a new post in which you see how you can use astro-icon to create a CSS-only Scroll to Anchor feature in your Astro app.

There’s also a tip on how you can make smooth scrolling more accessible.

Hope you find it useful!

https://t.co/ssZTfjWqM0

— Rodney (@askRodney) March 18, 2022

Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. As well as leaving a comment below, you can get in touch via @askRodney on Twitter and also askRodney on Telegram . Also, see further ways to get in touch with Rodney Lab. I post regularly on Astro as well as SvelteKit. Also, subscribe to the newsletter to keep up-to-date with our latest projects.

Thanks for reading this post. I hope you found it valuable. Please get in touch with your feedback and suggestions for posts you would like to see. Read more about me …

blurry low resolution placeholder image Rodney from Rodney Lab
TAGS:
ASTRO

Likes:

Likes

  • Caleb (he/him) #BlackLivesMatter profile avatar
  • Triffid TV 🇺🇦 profile avatar
  • Nate Moore profile avatar
  • Ashur Cabrera profile avatar
  • Brent Morton profile avatar
Likes provided by Mastodon & X via Webmentions.

Related Posts

blurry low resolution placeholder image Astro Server-Side Rendering: Edge Search Site

Astro Server-Side Rendering: Edge Search Site

plus
astro
<PREVIOUS POST
NEXT POST >
LATEST POST >>

Leave a comment …

Your information will be handled in line with our Privacy Policy .

Ask for more

1 Nov 2022 — Astro Server-Side Rendering: Edge Search Site
3 Oct 2022 — Svelte eCommerce Site: SvelteKit Snipcart Storefront
1 Sept 2022 — Get Started with SvelteKit Headless WordPress

Copyright © 2020 – 2025 Rodney Johnson. All Rights Reserved. Please read important copyright and intellectual property information.

  • Home
  • Profile
  • Plus +
  • Newsletter
  • Contact
  • Links
  • Terms of Use
  • Privacy Policy
We use cookies  to enhance visitors’ experience. Please click the “Options” button to make your choice.  Learn more here.