Styling Remix using Vanilla CSS
How to style a Remix app using Plain CSS
Wednesday, December 1, 2021
Table of Contents
TL;DR: Source and Demo
Link to the source code
Here's a live demo
Introduction
In my last blog post, I discussed how to perform a CRUD operation in a Remix app. In this blog post, I will discuss how to style a Remix app using only plain CSS.
Why to use plain CSS?
According to the official documentation
In general, stylesheets added to the page with
<link>
tend to provide the best user experience:
- The URL is cacheable in browsers and CDNs
- The URL can be shared across pages in the app
- The stylesheet can be loaded in parallel with the JavaScript bundles
- Remix can prefetch CSS assets when the user is about to visit a page with
<Link rel="prefetch">
.- Changes to components don't break the cache for the styles
- Changes to the styles don't break the cache for the JavaScript
Cleaning up styles from boilerplate
bash12
Global Styling
Recreate the app/styles/global.css
file
Some CSS defaults I copied from the official documentation with some adjustments.
app/styles/global.css1234567891011121314151617181920212223242526272829303132333435363738394041424344
We'll modify app/root.tsx
to import the global stylesheet; then, export the style in a links
function.
app/root.tsx12345678910111213
After Adding the links function, nothing changes in the app. If we check the elements inspector, there is no link tag inside the <head>
tag.
Add Links
to head
Since Remix will only do what we tell it to do, we need to inform it to render the links we exported in our pages using the Links
compnent.
app/root.tsx123456789101112131415
The changes above will yield to:
TRIVIA: We can technically put
<Links/ >
anywhere inside thehtml
tag; however, here's a reason why you should not do it.
Styling a Route
For now, we're not aiming to get the best design award; we'll just apply some styles for the sake of making it look different.
Create the /words
route styles
app/styles/words.css12345678910111213
Add class name definition inside the component
app/routes/words.tsx123456789101112131415
export the links function
app/routes/words.tsx1234567891011121314
The changes above will yield to:
NOTE: every child route of
/words
will inherit the styles exported inapp/routes/words.tsx
Styling a child Route
Since both our /add
and /edit/$id
routes use a form, let's create a shared css file.
I'm too lazy to think of a great design. Let's simply add a border.
app/styles/words/shared.css12345
Now, let's expose the style by exporting it in our links
function.
app/routes/words/add.tsx1234567891011
Here's the result:
After clicking the Add new word
button, the word form
will be styled as expected.
However, since the Add new word
is inside a form as well, that form will also have a border.
A straightforward way to fix this is to improve the specificity of the word form by adding a class name.
app/styles/words/shared.css12345
app/components/WordForm.tsx123456789
After adding the class name, our word form
styles won't leak to other form elements.
Sibling Routes Styling
I'm adding a temporary background color to demonstrate how sibling routes styling behave.
app/styles/words/shared.css12345678
After navigating from /add
to /edit/$id
, the word form
styling is removed in the head
; The reason why styles were not applied to the /edit/$id
form.
The reason is simple; sibling routes do not share styles with other sibling routes.
To fix the issue, we need to apply similar change to app/routes/words/edit.$id.tsx
app/routes/words/edit.$id.tsx1234567891011
Now it's working as expected. There's no magic or whatever, just pure JavaScript doing its thing.
Styling a reusable component
Revert changes in
app/styles/words/shared.css
,app/routes/words/add.tsx
, and ,app/routes/words/edit.$id.tsx
We were able to share styles between sibling routes. However, this is not the right approach for this app.
The change we made was specific to a form and not a page, so we'll make the changes in the word form component.
Create a new folder word-form
under app/components
Create the word form styles
app/components/word-form/styles.css123456789101112
Rename WordForm.tsx
to index.tsx
and move it to app/components/word-form
Export the declaration of our links
magic function
app/components/word-form/index.tsx1234567891011121314151617
Uh oh! The styles are not applied to the word form
component. Now, we only have 2 link
tag in the head
It seems the magical behavior of export const links
only applied to routes
To fix the issue, we need to propagate the styles to a route
app/routes/words/add.tsx123456
Apply the same thing in
app/routes/words/edit.$id.tsx
The changes above will yield to:
Styling custom basic HTML Element
Q: So how do we style a custom basic HTML element using CSS?
A: The same as for
word form
. Although, we need to propagate more until the reference reaches aroute
.
Custom Button
app/components/basic/button/index.tsx123456789101112131415161718192021
app/components/basic/button/styles.css12345678910
Other custom elements
There is nothing fancy for other elements, so you can view the sources here for input
, select
, and textarea
.
Here is how we can propagate the components used in the word form to the routes
app/components/word-form/index.tsx12345678910111213141516171819
New Word Form Component
After replacing the default html elements with our custom ones, our word form will look like this:
app/components/word-form/index.tsx1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
The changes above will yield to:
Dark Mode
There are many ways to implement dark mode.
This example will use prefers-color-scheme
to update the CSS variables when a media query is satisfied.
Create app/styles/dark.css
file with the following content:
app/styles/dark.css123456
Update app/root.tsx
file with the following content:
app/root.tsx1234567891011121314151617
The changes above will yield to:
Responsive Layout
We'll use the same approach in the dark mode example to change the layout depending on the result of a media query.
Create sm-words
app/styles/words-sm.css12345678
Use in app/routes/words.tsx
app/routes/words.tsx1234567891011121314151617
The changes above will yield to:
Conclusion
Styling Remix using Vanilla CSS is a fun exercise for me.
I was able to review concepts that I almost forgot due to the convenience of using third-party libraries.
I have fine-grain control over the styling of my routes and components, and I know that I only ship styles that will be used.
Less code to transmit to the Network means faster load times.
However, I'm not a fan of working in vanilla CSS, which is why in this project's next iteration, I will be integrating either Tailwind
or Emotion
.
What's next?
- Styling using
Tailwind
orEmotion
- Authentication
- Error handling
- SEO
- Deeply nested routes