Filtered by tag:css

csscustom-elements
article image
7 minutes read

How to create good looking custom scrollbars

In my opinion one of the most difficult part in frontend is to make the layout look similar in every browser and that's because we have CSS Resets, but scrollbars can be one of the points we forget but can makes a huge difference to users. ## Introduction At my first year as developer, I always will remember a gaffe committed when introducing some new features I had developed to the CEO's company. Well, I use Mozilla Developer Edition to code, but at that time I forgot users uses Chrome in majority. <Img src="https://github.com/fescherer/blog/assets/62115215/8be10ac9-bb0f-47d5-93b7-bed3144ad9fe" source="https://gs.statcounter.com" name="Browser Market Share Worldwide Dec 2022 - Jan 2024" alt="Statcounter graph showing Chrome are used by 70% of the users" /> So the CEO. Yes, the scrollbar's feature was horrible in Chrome because the scrollbars are not aesthetic as Firefox. In the end, I did not got any in any trouble, the CEO only just pointed out and I did the fix in the next patch. I learnt to never forget scrollbars again, it cannot be neglected. ## Problems The main issue with scrollbars it is different styles in each browser which makes really hard to us track how the final layout is gonna be delivered to the user. And is not only the visuals, some browsers make the scrollbars take a size of the container, which can lead to layout looking weird. We gonna follow the name convention in [this fantastic article by Zach Leatherman - Two Browsers Walked Into a Scrollbar](https://www.filamentgroup.com/lab/scrollbars/): <Blockquote cite="https://www.filamentgroup.com/lab/scrollbars"> - Obtrusive scrollbars: scrollbars that take up screen real estate. These do not overlay on top of the content, but appear next to it. - Unobtrusive scrollbars: scrollbars that sit on top of the content. These don't subtract screen real estate away from the container they belong to. </Blockquote> I personally would prefer that every browser had unobtrusive scrollbars most because we as developer would not need to worry about taking some space in our layout. But I know that's not possible and probably would ruin a lot of layouts out there. Don't get me wrong, I am not saying some browser is better than other, I really think that every browser has some kind of problem when talking about scrollbars. Let's point out: ### Mozilla Firefox Firefox has unobtrusive and really aesthetic scrollbar which is great but I don't like how only "grows" the scrollbar on hover, not the container, but the scrollbar. I think if it has to grow, that should grow on hover the content to be easy accessed and not the scrollbar. Like if I already have reached the scrollbar, it does not need to grow, right? The grow is to make the bar more accessible... <Img src="https://github.com/fescherer/blog/assets/62115215/ad1747c2-699f-492f-9f07-be5b8f2f97aa" name="Mozilla Firefox default scrollbar" alt="Gif showing that Firefox only grows the scrollbar if hovered IN the scrollbar" /> ### Chromium Browsers I love chromium browsers specially Chrome and Opera GX, I use them both in different situations, like coding I like Chrome and Mozilla Firefox Developer, but for daily routines I prefer Opera GX. But most of the scrollbars are not that great. First, they are obtrusive which for sole already brings some problems. Second, it is so wider even if user is not hovering the content, which is really ugly... <Img src="https://github.com/fescherer/blog/assets/62115215/d0c6360e-0546-4999-a296-8c11c2f62947" name="Chrome default scrollbar" alt="Gif showing the default behavior scrollbar in Mozilla Firefox" /> ### Edge One scrollbar I really like is from Edge Browser, yes, the successor of Internet Explorer. I think Edge has a lot of similar stuff with Chrome and the scrollbar is no different. Edge still has a wide scrollbar, but the borders are rounded which gives like a Firefox feeling. I really like, but I think could be better if were a little thinner. <Img src="https://github.com/fescherer/blog/assets/62115215/64badb1a-3fb8-4174-8c73-ba4222560296" name="Edge default scrollbar" alt="Gif showing the default behavior scrollbar in Edge" /> ## Mobile In mobile we have a little different problem. In my opinion mobile, the scrollbar can have the same problems above depending on which mobile browser you are, but with additional that scrollbars are only shown if you are actually scrolling, how I am supposed to know if that container has scroll if does not have a scrollbar? ## Objectives In this article I gonna show you how you can make your scrollbars aesthetic as can be in many different browsers, only using CSS. I am already warning you that is not possible to make like a reset to them, like we have done to `h1`, `p` and so on. CSS has its limitations and to achieve the fully goal probably will you need some Javascript code or maybe create all the logic with it. The method I am gonna show you is relative simple and can works on most browsers like Mozilla Firefox, Google Chrome, Brave, Opera GX, Edge and Safari, there will be an image with properties browser support in the end of the article. I think that's enough of talk, let's code. ## Solution First, please, do not ever style the main scrollbar. This is not standard, this do not need to follow your layout, it's good to look native to users do get lost. I think is good to style your scrollbars inside something you created, because this is "part" of your layout, different from the main scrollbar that is "part" of browser's layout. Did get it? Well, for the solution, I searched a lot and got really frustrated when finding out the perfect solution do not exist, but we can came to a really nice result. Most of the sites uses some kind of Javascript or very difficult CSS approaches, so I filtered out the really necessary styles and merge them in a class `styled-scrollbars` you can use in the overflowing container. Most of these styles were gotten from [Reddit's aside menu](https://www.reddit.com). ## CSS code There is no error, we gonna use two properties for Firefox (`scrollbar-color` and `scrollbar-width`) and some webkit for Chromium's browsers. ``` css /* Firefox Only */ /* Approaches mobile first, because here, the scrollbar-color will be set and always will be that color, but on desktop, that we have hover effect, we can give a transparent color, but how this is mobile first, we give the color here, and treat the desktop in other style */ .styled-scrollbars { scrollbar-color: var(--some-really-nice-color-here); scrollbar-width: thin; } /* Chrome and others Browsers */ .styled-scrollbars::-webkit-scrollbar { width: 4px; height: 4px } /* Chrome and others Browsers */ .styled-scrollbars::-webkit-scrollbar-track { background: transparent; } /* With device has hover (Desktop or TV, etc) give a little effect on hover */ @media (hover: hover) { .styled-scrollbars { scrollbar-color: var(--some-really-nice-color-here-not-hover) transparent ; scrollbar-width: thin; border-radius: 8px; transition: cubic-bezier(0.165, 0.84, 0.44, 1) 500ms all ; } .styled-scrollbars:hover { scrollbar-color: var(--some-really-nice-color-here) transparent } .styled-scrollbars:hover::-webkit-scrollbar-thumb { background: var(--some-really-nice-color-here); border-radius: 8px; -ms-overflow-style: none; scrollbar-width: thin; } } ``` To find the browser's support, please look at: - [webkit-scrollbar](https://developer.mozilla.org/en-US/docs/Web/CSS/::-webkit-scrollbar) - [scrollbar-color](https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color) - [scrollbar-width](https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-width) ## Final results <Img src="https://github.com/fescherer/blog/assets/62115215/4af5a2b1-b06a-444d-a135-3b5f85472089" name="Mozilla Firefox" alt="Gif showing the result of aesthetic scrollbar in Firefox" /> <Img src="https://github.com/fescherer/blog/assets/62115215/24d9ca86-69a8-4335-9dea-b68f0278701d" name="Chrome" alt="Gif showing the result of aesthetic scrollbar in Chrome" /> <Img src="https://github.com/fescherer/blog/assets/62115215/9e114c2b-06ca-459b-aa95-aecc4b02e57d" name="Edge" alt="Gif showing the result of aesthetic scrollbar in Edge" /> ## Additional tips One very interesting stuff I found by searching content for this article is a solution for components that has a special css for hovering effects, is to use `media query hover` to only enable this on hover support devices. You can checkout the [full video by the master of CSS Kevin Powell](https://www.youtube.com/watch?v=uuluAyw9AI0). I highly recommend. ## Conclusion In this article we saw a very clean way, consistent and CSS only to style your scrollbars to be aesthetic as possible between many different browsers like Mozilla Firefox, Google Chrome, Brave, Opera GX, Edge and Safari. Of course this approach is not 100% effective in all platforms, but works on most of them. Have a nice day. ๐Ÿ˜Žโœจโœจโœจ๐Ÿ˜Ž

cssthemestailwind
article image
10 minutes read

Tailwind Strategies for themes

In this article I will talk about some strategies you can use to create themes on Tailwind, a way to write CSS in utility classes, like if you want to say `display: flex`, you can just give a *classname* of `flex`. Of course this is a very simple example, but you can do so much more and everything is customizable. [Tailwind CSS](https://tailwindcss.com) as the docs says: <Blockquote cite="https://eslint.org/docs/latest/use/getting-started"> A utility-first CSS framework packed with classes like `flex`, `pt-4`, `text-center` and `rotate-90` that can be composed to build any design, directly in your markup. </Blockquote> ### What is Tailwind CSS To understand Tailwind CSS is good to know how it was created. Well, the framework was created by [Adam Wathan](https://twitter.com/adamwathan) because he wanted to use a [Bootstrap](https://getbootstrap.com) kinda syntax with [Less processor](https://lesscss.org), as you may know Bootstrap has build in classes for `buttons`, `forms`, etc. Tailwind in other hand has just utility classes, like `flex`, `backgrounds`, `paddings`, `margins`, etc. That's why in the first versions of Tailwind, it has some of the classes like Bootstrap, but from time on Adam switched to more generic classes. He also needed to change the processor to PostCSS, but you can find the whole history at [OfferZen Origins's channel](https://www.youtube.com/watch?v=1x7HlvSfW6s). For me, frameworks that's uses utility classes are the best ones, it can be strange at first but the productivity grows so much using this strategy that I cannot stop using. ### Popularity For popularity Tailwind is one of the favorite CSS Framework out there. You can see this in [State of CSS Survey](https://2023.stateofcss.com/en-US/css-frameworks): <Img src="https://github.com/fescherer/blog/assets/62115215/23055cb4-026e-4f25-b287-e3556251faf3" name="State of CSS" source="https://2023.stateofcss.com/en-US/css-frameworks" alt="Survey from State of CSS showing the is one of most used technologies for styling" /> They have a lot of interesting data. I suggest you to take look and see for yourself. ### How to use As said before, Tailwind uses utility classes, so you will add them into your html as so: ```html {1-2} <div className="m-4 flex items-center justify-center"> <span>Item 1</span> <span>Item 2</span> </div> ``` When I started coding, the company used [Angular](https://angular.io) with kinda of utility classes. We a had a big style sheet containing all the most used classes like `padding` or `margin` and them, if we would need some more specific style, we create a class just for it. I grow up and for my experience, don't do that, try to stay just with utility classes, it way more easy to maintain. You can find the list of utility classes available [in the docs](https://tailwindcss.com/docs/utility-first). ## What is theme Theme is a set of characteristics that build an idea. I can be applied to an environment to give the feeling of that characteristics. So you can made a party using a Halloween theme, so everyone and the party probably will have characteristics from Halloween. In design is the same thing, we can made certain [rules that build in a theme](https://www.uxpin.com/studio/blog/design-system-theming), like font sizes, spaces, images and colors. This can be used for accessibility advantages or just to made a design some unique like a special version for christmas. It became very popular for all designs to have at least a version called `dark` with more darker colors and a `light` with, of course, lighter colors. So it is very important to know how you can handle this. ## Strategies for theming in Tailwind In this tutorial, I will show only themes that change colors, that's because I think changing the font-size or spaces can be tricky and not so great in the final product. Tailwind already has a [theme build in support](https://tailwindcss.com/docs/dark-mode) but I think is not ideal mostly if you want to add more than just dark and light theme. So that's why I gonna show you some strategies you can use to add more themes for Tailwind CSS. ### Using external libraries There a lot of libs created by the community to solve the problem of multiple themes in Tailwind. Some examples would be: - [Tailwind CSS Theme Variants](https://github.com/JNavith/tailwindcss-theme-variants); - [Tailwind Themer](https://github.com/RyanClementsHax/tailwindcss-themer); - [TW Colors](https://github.com/L-Blondy/tw-colors); - Many more... All have a very similar concept and way to solve this by enable to create themes on `Tailwind.config.ts`, as an example of configuring three themes dark, light and forest using [TW Colors](https://github.com/L-Blondy/tw-colors#add-more-themes): ```typescript const { createThemes } = require('tw-colors'); module.exports = { content: ['./src/**/*.{html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], plugins: [ createThemes({ light: { 'primary': 'steelblue', 'secondary': 'darkblue', 'brand': '#F3F3F3', }, dark: { 'primary': 'turquoise', 'secondary': 'tomato', 'brand': '#4A4A4A', }, forest: { 'primary': '#2A9D8F', 'secondary': '#E9C46A', 'brand': '#264653', }, }) ], }; ``` It is a great improve on my point of view and you can certainly use it, but for me I would like to make my own code in my own way. ### CSS Variables TW Colors uses CSS variables to make the themes, so I thought I could use too as I already made a theme logic using them when working with Angular 2 years ago. Then I started thinking what are my needs and how can I solve them. The first thing was that I want as many themes as possible- So I would need to provide to every theme into different files as I wanted a lot of them, staying at the same file would not be very scalable. Second thing I wanted is to control the theme based on a html attribute `data-theme` as: ```html <html data-theme="dark"></html> ``` With this in mind, I thought on a simple solution: Use CSS variables, and change the value of them using Javascript. To start this I overwrite [Tailwind default colors](https://tailwindcss.com/docs/customizing-colors) with my colors in `Tailwind.config.ts` using CSS variables syntax as: ```typescript import type { Config } from 'tailwindcss' import defaultTheme from 'tailwindcss/defaultTheme' const config: Config = { content: [ './src/**/**/*.{js,ts,jsx,tsx,mdx}', ], theme: { colors: { 'primary': 'var(--primary)', 'secondary': 'var(--secondary)', 'text': 'var(--text)', 'title': 'var(--title)', 'foreground': 'var(--foreground)', 'background': 'var(--background)', 'background-card': 'var(--background-card)', 'text-on-primary': 'var(--text-on-primary)', 'text-hover': 'var(--text-hover)', 'primary-hover': 'var(--primary-hover)', 'code-header': 'var(--code-header)', 'transparent': 'transparent', 'current': 'currentColor', }, } } export default config ``` A little observation about two colors `transparent` and `current` that are been used as constants and not variables values, because I do not want to change them when the theme is changed. Continuing after colors configuration, I need to declare these variables and default values for them into some place so I went to `global.css` where [Tailwind is init](https://tailwindcss.com/docs/installation/using-postcss) and then declare the variables into a root [pseudo-class](https://developer.mozilla.org/en-US/docs/Web/CSS/:root): ```css @tailwind base; @tailwind components; @tailwind utilities; :root { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #ffffff0a; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` If you want, you can add a accessible [medias prefers color scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). That almost it, now you just need to add in some css file the values for each theme like so: ```css :root[data-theme='dark'] { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #ffffff0a; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` To make a better organization I like to make one file for each theme and then export all in one file. <Img src="https://github.com/fescherer/blog/assets/62115215/672b8aae-169a-4028-ace2-0f5482005252" name="Folder structure" alt="Folder structure" /> ```css /* themes/variants/dark.css */ :root[data-theme='dark'] { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #131313; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` ```css /* themes/variants/light.css */ :root[data-theme='light'] { --primary: #359967; --secondary: #4d9470; --text: #090a0a; --title: #191b1a; --foreground: #dfdfdf; --background: #ffffff; --background-card: #dfdfdf; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } ``` ```css /* themes/index.css */ @import './variants/dark.css'; @import './variants/light.css'; ``` This way I can import the `index.css` file with all the imports already set up. The last part, we need to make the logic to change themes. To do this first you need to map all your themes into an array. ```typescript /* themes/themes.theme.ts */ export const themes = [ 'dark', 'light', ] ``` Lastly just change the value of the `html` attribute `data-theme` with Javascript and voilร . ```javascript document.getElementsByTagName('html')[0].setAttribute('data-theme', 'light') ``` ## SSR Themes preference If you analyze my blog you would probably noticed that theme preference is storage somewhere and then loaded before even the page. How that's possible? The key word is [`cookies`](https://web.dev/understanding-cookies). These type of data is storage and can be access by the server, not only the browser like [`local storage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage), using this, we can load the theme in server and then bring to browser the storage one as default. There are many ways to manage cookies and I think that every framework has its own way to do that. In this tutorial I will teach you with [NextJS 13 using app router](https://nextjs.org) because it is what I use and have experience. First we need to install 1 dependency called [cookies-next](https://www.npmjs.com/package/cookies-next) because with the [new API of app router, we can only use cookies at server components](https://nextjs.org/docs/app/api-reference/functions/cookies) but this solves just one part of the equation the load can be at server components, but every time we use interaction, the component need to be client, so how am I supposed to save a cookie of user theme choice? That's why we use `cookies-next`, to manipulate cookies at client components. ```bash npm install cookies-next ``` Let's start in the root `layout.tsx`. ```jsx export default function RootLayout({ children, }: { children: React.ReactNode }) { const cookieTheme = cookies().get('data-theme') const theme = themes.includes(cookieTheme?.value ?? '') ? cookieTheme?.value : 'dark' return ( <html lang="en" data-theme={theme}> <body> <main> {children} </main> </body> </html> ) } ``` Using the code above you can load the cookie `data-theme` and then pass to `html`. Now to change this just use the code Javascript that I said to you earlier. ```jsx // Here you can storage these values together with the themes like I showed you earlier. const themes = [ 'dark', 'light', ] function handleTheme(theme: string) { document.getElementsByTagName('html')[0].setAttribute('data-theme', theme) } export function ThemeSelector() { return ( <div> { themes.map((theme) => ( <button key={theme} onClick={() => handleTheme(theme)}> {theme} </button> )) } </div> ) } ``` ## User preferences - prefers-color-scheme, prefers-reduced-motion, etc One last thing you can do to make your code even better is to add a default theme based on user preference, it is very common to add a option on theme selector saying `default` or `system`, this is a theme based on user's system theme. So that's really import to make our interface looks like the user's on first time accessing our site. To do that is very simple, just add a [CSS query prefers-color-scheme](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme). ```css /* themes/variants/system.css */ @media (prefers-color-scheme: dark) { :root { --primary: #51C28A; --secondary: #74DFAA; --text: #DDDDDD; --title: #EAEAEA; --foreground: #282929; --background: #131313; --background-card: #282929; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #21222C; } } @media (prefers-color-scheme: light) { :root { --primary: #359967; --secondary: #4d9470; --text: #090a0a; --title: #191b1a; --foreground: #dfdfdf; --background: #ffffff; --background-card: #dfdfdf; --text-on-primary: #FFFFFF; --text-hover: #888888; --primary-hover: #409c6e; --code-header: #f3e9de; } } ``` ## Conclusion In this article I told you a little about the history behind Tailwind CSS creation, with the explain about what are themes and how people use them with Tailwind. For last how I would implement themes by myself using Tailwind and NextJS SSR feature to load theme in the server rendering time. Thank you for your time. Have a nice day. ๐Ÿ˜Žโœจโœจโœจ๐Ÿ˜Ž