☺️ Svelte Shy Header: What on Earth is a Shy Header? #
I wanted a Svelte shy header solution to add to a site I was working on and came across a Tweet on a solution by Jake Archibald and Robert Flack from Google’s Chromium team. I needed the solution for a demo site; adding some text in the header to describe the setup a little. As an extra feature to improve user experience (UX), I wanted the header to re-appear if the user scrolled upwards again. That is, even if they were already halfway down the page. The idea being to save them having to scroll all the way back up to the top to see the site setup blurb.
I saw no need to use a library as the implementation was fantastic. Jake and Robert’s solution leaned heavily on modern CSS, and I wondered how much I could Sveltify. For example, make use of Svelte style directives, element dimension bindings and what not. Basically a kitchen sink, all-out Svelte approach.
If you are new to Svelte, you might find this post useful as a showcase of some of Svelte’s templating and styling features. If you have more experience, you probably have some ideas on how to make it even Sveltier! Reach out in a comment below if you do.
Original HTML / CSS / JavaScript Solution #
Jake’s full solution is on CodePen and uses plain HTML, CSS and JavaScript. Here is a summary:
🔫 Svelte Shy Header: Sveltification Plan of Action #
Here’s my initial thinking on the Sveltification process:
- you can easily bind a JavaScript variable to an element’s height with Svelte’s dimension bindings,
- Svelte has some nice syntactic sugar for adding event listeners. We will try to work these in for the scroll and resize events,
- there’s also a Svelte way to bind an element to a JavaScript variable. This will simplify any element style updates we keep in the JavaScript code. We will try to move some of them down to the element using style directives.
Enough talk, time for some action.
🧱 Svelte Shy Header: Code Along #
If you are new to Svelte, hopefully this little project can provide a gentle introduction. Here’s how you can spin up a new project. Start by running these commands in the Terminal:
You will get some options, choose a Skeleton project and whatever you like for the others. The last command will start up a dev server and spit out a URL you can paste into your browser. We will only create or change two files, so just paste the code in when we get to that point. Also, make sure you play with, breaking and repairing stuff, to understand better what it is doing!
🏠 Home Page #
I thought, why not throw in a background CSS pattern? I opted for customizing the Colorful Stingrays pattern from www.svgbackgrounds.com . Here is the full code for the home page (replace existing content in src/routes/+page.svelte
if you are coding along):
This is a typical Svelte file with three sections: script
tag where
we run the JavaScript Code, the Svelte markup, then the style
tag.
We place the Header in its own component file (imported in line 2
). This makes it easier to recycle it for use on other pages. On top, you can easily create a Svelte component library, letting you share the code between your other Svelte projects.
🙈 Svelte Shy Header Component #
If coding along, create a src/lib/components/Header.svelte
file (you
will need to create a couple of new folders). That is the file we look at now. This file will eventually
have the three sections we have in the home page, but we will start with the middle section:
In line 1
, we add the event listeners for scroll
and resize
events, but the Svelte way! fixHeaderOffset
is the function we will invoke on these events. We will define it later.
Next, in like 3
, we have the header-shifter
div. We keep the original class name, but also add:
As you might guess, we are setting the element’s height here, using CSS. This is a Svelte style directive. In place of height
you can have any regular CSS property or even
a custom CSS property (often called CSS variable). headerShifterHeight
is a number variable which we will define in the script
tag. The
px
is just to add expected units for valid CSS. An alternative would
have been to write something like this, making use of the element’s style attribute and JavaScript
template strings:
It is Svelte best practice to avoid this, just because, using the original code, the Svelte
compiler optimizes the output. It will only update styles on the element if headerShifterHeight
changes.
header
Element #
Let’s see the header div now (lines 4
– 9
above). Here it is again:
We just saw a style directive, and the class attribute is nothing new. That leaves the two bind
statements. The first bind:this={header}
is another Sveltism.
This lets us bind the element itself to the JavaScript variable header
. That saves us writing out a query selector when we get to writing the script tag. It’s
just syntactic sugar making the code a little cleaner.
The second bind
directive it where we link the header’s height
to the headerHeight
JavaScript variable. This will save us calling
getBoundingClientRect
later in the script
tag.
💄 Svelte Style #
Paste this code in if you are coding along
We are adding a touch more style vs. the original version, but not really changing anything
fundamentally. The main change is the --_computed-height
custom CSS
property in line 18. The CSS custom property spec lets us specify a fallback value as a second argument.
CSS will use this, second, value if --computed-height
is not set. You
will notice we do not set it anywhere here in the CSS. As in the original solution, we set it using
JavaScript. However, if the user has JavaScript disabled in their browser, we can rely on the fallback
value we provide here. This is not strictly necessary (the property is only used in styles added by
JavaScript), but provides a nice demo of how you can define defaults. Props to Geoff Rich for this technique .
🐇 Svelte Shy Header: Java Script #
Finally, we can see the much trailed script
tag! Once again, paste
this in (right at the top of src/lib/components/Header.svelte
) if
you are coding along.
I should have mentioned I am using TypeScript. Drop the lang=ts
and
type annotations (:HTMLDivElement
& :number
) in lines 13
, 20
and 21
if you opted for JavaScript instead.
Let’s see what we have. In lines 8
and 9
we declare the variables which we bound in the template code. Then in line 10
we declare and initialize the headerShifterHeight
which we also
used in the markup. Just by using let
here, we make the variable reactive.
This means when it changes, Svelte will automatically update the style directives (as well as anything
else linked) we added earlier.
The fixHeaderOffset
function in the original implementation remains,
though we have a few differences. We got rid of the getBoundingClientRect
call to get the header height, as we now have the value bound to headerHeight
by Svelte. We renamed y
to headerShifterHeight
, just so the template code is a little clearer. Then, remember we use style directives to set
the height on the header-shifter
element and bottom margin on the header
element, so removed those two lines.
Svelte onMount
#
Finally, all we have left is onMount
. This is a Svelte lifecycle
method. An alternative it to use Svelte actions. We explore both in the tutorial on creating a Svelte video blog , where we use these in lazy loading video and images.
We set the CSS style props here, instead of using style directives just because, we will not need
them defined if the user has JavaScript disabled. In this instance, we want a regular position: relative
header, rather than a sticky
one managed in JavaScript. Since the
onMount
function will not load with JavaScript disabled, the CSS override
here on header.style.position
is only set with JavaScript enabled.
The top
and bottom
CSS properties are
only relevant when JavaScript is enabled (we use relative
positioning
otherwise). Taking that into account, we set those two properties in onMount
rather than using style directives. Note how we use --_computed-height
instead of --computed-height
. This is because, as we mentioned
above, the former will have a default value, available before we first calculate and set --computed-height
.
Finally, we put the fixHeaderOffset
call in onMount
because we need to access the bound JavaScript variables (header
and headerHeight
) when the function is invoked. These are only
available once we mount the component. So placing the initial fixHeaderOffset
call here avoids calling it when those variable are still undefined.
🗳 Poll #
🙌🏽 Svelte Shy Header: Wrapup #
In this Svelte shy header post. We saw some useful Svelte methods and how they relate to those you might see in plain HTML / CSS / JavaScript sites. In particular, we have touched on:
- how to use Svelte style directives for component styling and when another approach might be preferred;
- full code for implementing a Svelte shy header as a reusable component; and
- how to bind component dimensions to JavaScript variables with Svelte.
As a next step, you might consider following one of the tutorials to learn more about Svelte. If you already know the basics, also try adding this stick header to a Svelte component library. You might also want to take CSS custom properties to the next level, setting them in Svelte actions. Let me know what you end up creating. You can drop a comment below or reach out for a chat on Element as well as Twitter @mention
You can see the full code for this Svelte shy header project, in all its glory in the Rodney Lab Git Hub repo .
🏁 Svelte Shy Header: Summary #
Is it always better to use Svelte style directives instead of setting styles in JavaScript? #
- Svelte style directives let you add styling directly to an element. They can add optimizations over using a `style` attribute on the element. Normally, you only use style directives when the CSS property you have is dynamic. As an example, the element changes colour when something changes in JavaScript code. Generally, you will benefit from Svelte in-build optimizations by using style directives instead of setting style on the element in JavaScript directly. One time you might want to use JavaScript, though, is if you need to style differently when JavaScript is enabled vs. when the user has disabled JavaScript. We saw an example of this, setting position to `sticky` (from `relative`) on a peekaboo header with JavaScript enabled.
Can you set default CSS custom property values in Svelte? #
- We saw an example where we wanted to set a custom property in Svelte code and use that property independently of CSS. With JavaScript disabled, the property would be undefined. We saw a technique which let us set a default. The CSS property we wanted to use was --computed-height. By introducing a second CSS property `--_computed-height` we could set a default. We did this by using the CSS feature which lets us set a fallback for custom properties, using an optional second argument. In our case, we set `--_computed-height: var(--computed-height, 165px);`. Now, once we set `--computed-height` in our JavaScript code, `--_computed-height` would take on that value. However, while `--computed-height` remains undefined, `--_computed-height` gets 165px, the default. We saw if we reference `--_computed-height` in our code, we will never get an undefined value back.
How can you access element dimension in Svelte? #
- Svelte lets you bind JavaScript values to element dimensions as well as the elements themselves. If, for example, we wanted to use the height of an element in JavaScript, we can use Svelte’s in-built syntactic sugar. First, we add `bind:clientHeight={headerHeight}` amid the elements attributes in its markup. This sets the JavaScript variable `headerHeight` to the element height. To finish off, we just need to declare the variable in our script tag: `let headerHeight;`. That’s all there is to it. You can set width too. There is a further shortcut. For that to work, though, the JavaScript variable needs to be named `clientHeight`. In this case, we just add `bind:clientHeight` on the element.
🙏🏽 Svelte Shy Header: Feedback #
If you have found this post useful, see links below for further related content on this site. I do hope you learned one new thing from the video. Let me know if there are any ways I can improve on it. I hope you will use the code or starter in your own projects. Be sure to share your work on Twitter, giving me a mention, so I can see what you did. Finally, be sure to let me know ideas for other short videos you would like to see. Read on to find ways to get in touch, further below. If you have found this post useful, even though you can only afford even a tiny contribution, please consider supporting me through Buy me a Coffee.
Just dropped a new post on the whole challenge of Sveltifying the original HTML / CSS / JavaScript code.
— Rodney (@askRodney) August 5, 2022
Using a little style directive, component binding and event listening the ❤️ Svelte way (as well as dimension binding).
Hope you find it useful!
https://t.co/7WIjc67LjN
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 SvelteKit as well as Search Engine Optimization among other topics. Also, subscribe to the newsletter to keep up-to-date with our latest projects.