Tag: Solid

Introduction to the Solid JavaScript Library

Solid is a reactive JavaScript library for creating user interfaces without a virtual DOM. It compiles templates down to real DOM nodes once and wraps updates in fine-grained reactions so that when state updates, only the related code runs.

This way, the compiler can optimize initial render and the runtime optimizes updates. This focus on performance makes it one of the top-rated JavaScript frameworks.

I got curious about it and wanted to give it a try, so I spent some time creating a small to-do app to explore how this framework handles rendering components, updating state, setting up stores, and more.

Here’s the final demo if you just can’t wait to see the final code and result:

Getting started

Like most frameworks, we can start by installing the npm package. To use the framework with JSX, run:

npm install solid-js babel-preset-solid

Then, we need to add babel-preset-solid to our Babel, webpack, or Rollup config file with:

"presets": ["solid"]

Or if you’d like to scaffold a small app, you can also use one of their templates:

# Create a small app from a Solid template npx degit solidjs/templates/js my-app   # Change directory to the project created cd my-app   # Install dependencies npm i # or yarn or pnpm   # Start the dev server npm run dev

There is TypeScript support so if you’d like to start a TypeScript project, change the first command to npx degit solidjs/templates/ts my-app.

Creating and rendering components

To render components, the syntax is similar to React.js, so it might seem familiar:

import { render } from "solid-js/web";   const HelloMessage = props => <div>Hello {props.name}</div>;   render(  () => <HelloMessage name="Taylor" />,  document.getElementById("hello-example") );

We need to start by importing the render function, then we create a div with some text and a prop, and we call render, passing the component and the container element.

This code then compiles down to real DOM expressions. For example, the code sample above, once compiled by Solid, looks something like this:

import { render, template, insert, createComponent } from "solid-js/web";   const _tmpl$   = template(`<div>Hello </div>`);   const HelloMessage = props => {  const _el$   = _tmpl$  .cloneNode(true);  insert(_el$  , () => props.name);  return _el$  ; };   render(  () => createComponent(HelloMessage, { name: "Taylor" }),  document.getElementById("hello-example") );

The Solid Playground is pretty cool and shows that Solid has different ways to render, including client-side, server-side, and client-side with hydration.

Tracking changing values with Signals

Solid uses a hook called createSignal that returns two functions: a getter and a setter. If you’re used to using a framework like React.js, this might seem a little weird. You’d normally expect the first element to be the value itself; however in Solid, we need to explicitly call the getter to intercept where the value is read in order to track its changes.

For example, if we’re writing the following code:

const [todos, addTodos] = createSignal([]);

Logging todos will not return the value, but a function instead. If we want to use the value, we need to call the function, as in todos().

For a small todo list, this would be:

import { createSignal } from "solid-js";   const TodoList = () => {  let input;  const [todos, addTodos] = createSignal([]);    const addTodo = value => {    return addTodos([...todos(), value]);  };    return (    <section>      <h1>To do list:</h1>      <label for="todo-item">Todo item</label>      <input type="text" ref={input} name="todo-item" id="todo-item" />      <button onClick={() => addTodo(input.value)}>Add item</button>      <ul>        {todos().map(item => (          <li>{item}</li>        ))}      </ul>    </section>  ); };

The code sample above would display a text field and, upon clicking the “Add item” button, would update the todos with the new item and display it in a list.

This can seem pretty similar to using useState, so how is using a getter different? Consider the following code sample:

console.log("Create Signals"); const [firstName, setFirstName] = createSignal("Whitney"); const [lastName, setLastName] = createSignal("Houston"); const [displayFullName, setDisplayFullName] = createSignal(true);   const displayName = createMemo(() => {  if (!displayFullName()) return firstName();  return `$  {firstName()} $  {lastName()}`; });   createEffect(() => console.log("My name is", displayName()));   console.log("Set showFullName: false "); setDisplayFullName(false);   console.log("Change lastName "); setLastName("Boop");   console.log("Set showFullName: true "); setDisplayFullName(true);

Running the above code would result in:

Create Signals   My name is Whitney Houston   Set showFullName: false   My name is Whitney   Change lastName   Set showFullName: true   My name is Whitney Boop

The main thing to notice is how My name is ... is not logged after setting a new last name. This is because at this point, nothing is listening to changes on lastName(). The new value of displayName() is only set when the value of displayFullName() changes, this is why we can see the new last name displayed when setShowFullName is set back to true.

This gives us a safer way to track values updates.

Reactivity primitives

In that last code sample, I introduced createSignal, but also a couple of other primitives: createEffect and createMemo.

createEffect

createEffect tracks dependencies and runs after each render where a dependency has changed.

// Don't forget to import it first with 'import { createEffect } from "solid-js";' const [count, setCount] = createSignal(0);   createEffect(() => {  console

Count is at... logs every time the value of count() changes.

createMemo

createMemo creates a read-only signal that recalculates its value whenever the executed code’s dependencies update. You would use it when you want to cache some values and access them without re-evaluating them until a dependency changes.

For example, if we wanted to display a counter 100 times and update the value when clicking on a button, using createMemo would allow the recalculation to happen only once per click:

function Counter() {    const [count, setCount] = createSignal(0);    // Calling `counter` without wrapping it in `createMemo` would result in calling it 100 times.    // const counter = () => {    //    return count();    // }      // Calling `counter` wrapped in `createMemo` results in calling it once per update. // Don't forget to import it first with 'import { createMemo } from "solid-js";'    const counter = createMemo(() => {        return count()    })      return (        <>        <button onClick={() => setCount(count() + 1)}>Count: {count()}</button>        <div>1. {counter()}</div>        <div>2. {counter()}</div>        <div>3. {counter()}</div>        <div>4. {counter()}</div>        <!-- 96 more times -->        </>    ); } 

Lifecycle methods

Solid exposes a few lifecycle methods, such as onMount, onCleanup and onError. If we want some code to run after the initial render, we need to use onMount:

// Don't forget to import it first with 'import { onMount } from "solid-js";'   onMount(() => {  console.log("I mounted!"); });

onCleanup is similar to componentDidUnmount in React — it runs when there is a recalculation of the reactive scope.

onError executes when there’s an error in the nearest child’s scope. For example we could use it when fetching data fails.

Stores

To create stores for data, Solid exposes createStore which return value is a readonly proxy object and a setter function.

For example, if we changed our todo example to use a store instead of state, it would look something like this:

const [todos, addTodos] = createStore({ list: [] });   createEffect(() => {  console.log(todos.list); });   onMount(() => {  addTodos("list", [    ...todos.list,    { item: "a new todo item", completed: false }  ]); });

The code sample above would start by logging a proxy object with an empty array, followed by a proxy object with an array containing the object {item: "a new todo item", completed: false}.

One thing to note is that the top level state object cannot be tracked without accessing a property on it — this is why we’re logging todos.list and not todos.

If we only logged todo` in createEffect, we would be seeing the initial value of the list but not the one after the update made in onMount.

To change values in stores, we can update them using the setting function we define when using createStore. For example, if we wanted to update a todo list item to “completed” we could update the store this way:

const [todos, setTodos] = createStore({  list: [{ item: "new item", completed: false }] });   const markAsComplete = text => {  setTodos(    "list",    i => i.item === text,    "completed",    c => !c  ); };   return (  <button onClick={() => markAsComplete("new item")}>Mark as complete</button> );

Control Flow

To avoid wastefully recreating all the DOM nodes on every update when using methods like .map(), Solid lets us use template helpers.

A few of them are available, such as For to loop through items, Show to conditionally show and hide elements, Switch and Match to show elements that match a certain condition, and more!

Here are some examples showing how to use them:

<For each={todos.list} fallback={<div>Loading...</div>}>  {(item) => <div>{item}</div>} </For>   <Show when={todos.list[0].completed} fallback={<div>Loading...</div>}>  <div>1st item completed</div> </Show>   <Switch fallback={<div>No items</div>}>  <Match when={todos.list[0].completed}>    <CompletedList />  </Match>  <Match when={!todos.list[0].completed}>    <TodosList />  </Match> </Switch>

Demo project

This was a quick introduction to the basics of Solid. If you’d like to play around with it, I made a starter project you can automatically deploy to Netlify and clone to your GitHub by clicking on the button below!

This project includes the default setup for a Solid project, as well as a sample Todo app with the basic concepts I’ve mentioned in this post to get you going!

There is much more to this framework than what I covered here so feel free to check the docs for more info!


The post Introduction to the Solid JavaScript Library appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,

Going From Solid to Knockout Text on Scroll

Here’s a fun CSS trick to show your friends: a large title that switches from a solid color to knockout text as the background image behind it scrolls into place. And we can do it using plain ol’ HTML and CSS!

This effect is created by rendering two containers with fixed <h1> elements. The first container has a white background with knockout text. The second container has a background image with white text. Then, using some fancy clipping tricks, we hide the first container’s text when the user scrolls beyond its boundaries and vice-versa. This creates the illusion that the text background is changing.

Before we begin, please note that this won’t work on older versions of Internet Explorer. Also, fixed background images can be cumbersome on mobile WebKit browsers. Be sure to think about fallback behavior for these circumstances.

Setting up the HTML

Let’s start by creating our general HTML structure. Inside an outer wrapper, we create two identical containers, each with an <h1> element that is wrapped in a .title_wrapper.

<header>    <!-- First container -->   <div class="container container_solid">     <div class="title_wrapper">       <h1>The Great Outdoors</h1>     </div>   </div>    <!-- Second container -->   <div class="container container_image">     <div class="title_wrapper">       <h1>The Great Outdoors</h1>     </div>   </div>  </header>

Notice that each container has both a global .container class and its own identifier class — .container_solid and .container_image, respectively. That way, we can create common base styles and also target each container separately with CSS.

Initial styles

Now, let’s add some CSS to our containers. We want each container to be the full height of the screen. The first container needs a solid white background, which we can do on its .container_solid class. We also want to add a fixed background image to the second container, which we can do on its .container_image class.

.container {   height: 100vh; }  /* First container */ .container_solid {   background: white; }  /* Second container */ .container_image {   /* Grab a free image from unsplash */   background-image: url(/path/to/img.jpg);   background-size: 100vw auto;   background-position: center;   background-attachment: fixed; }

Next, we can style the <h1> elements a bit. The text inside .container_image can simply be white. However, to get knockout text for the <h1> element inside container_image, we need to apply a background image, then reach for the text-fill-color and background-clip CSS properties to apply the background to the text itself rather than the boundaries of the <h1> element. Notice that the <h1> background has the same sizing as that of our .container_image element. That’s important to make sure things line up.

.container_solid .title_wrapper h1 {   /* The text background */   background: url(https://images.unsplash.com/photo-1575058752200-a9d6c0f41945?ixlib=rb-1.2.1&q=85&fm=jpg&crop=entropy&cs=srgb&ixid=eyJhcHBfaWQiOjE0NTg5fQ);   background-size: 100vw auto;   background-position: center;      /* Clip the text, if possible */   /* Including -webkit` prefix for bester browser support */   /* https://caniuse.com/text-stroke */   -webkit-text-fill-color: transparent;   text-fill-color: transparent;   -webkit-background-clip: text;   background-clip: text;      /* Fallback text color */   color: black; }  .container_image .title_wrapper h1 {   color: white; }

Now, we want the text fixed to the center of the layout. We’ll add fixed positioning to our global .title_wrapper class and tack it to the vertical center of the window. Then we use text-align to horizontally center our <h1> elements.

.header-text {   display: block;   position: fixed;    margin: auto;   width: 100%;   /* Center the text wrapper vertically */   top: 50%;   -webkit-transform: translateY(-50%);       -ms-transform: translateY(-50%);           transform: translateY(-50%); }  .header-text h1 {   text-align: center; }

At this point, the <h1> in each container should be positioned directly on top of one another and stay fixed to the center of the window as the user scrolls. Here’s the full, organized, code with some shadow added to better see the text positioning.

Clipping the text and containers

This is where things start to get really interesting. We only want a container’s <h1> to be visible when its current scroll position is within the boundaries of its parent container. Normally this can be solved using overflow: hidden; on the parent container. However, with both of our <h1> elements using fixed positioning, they are now positioned relative to the browser window, rather than the parent element. In this case using overflow: hidden; will have no effect.

For the parent containers to hide fixed overflow content, we can use the CSS clip property with absolute positioning. This tells our browser hide any content outside of an element’s boundaries. Let’s replace the styles for our .container class to make sure they don’t display any overflowing elements, even if those elements use fixed positioning.

.container {   /* Hide fixed overflow contents */   clip: rect(0, auto, auto, 0);    /* Does not work if overflow = visible */   overflow: hidden;    /* Only works with absolute positioning */   position: absolute;    /* Make sure containers are full-width and height */   height: 100vh;   left: 0;   width: 100%; }

Now that our containers use absolute positioning, they are removed from the normal flow of content. And, because of that, we need to manually position them relative to their respective parent element.

.container_solid {   /* ... */    /* Position this container at the top of its parent element */   top: 0; }  .container_image {   /* ... */  /* Position the second container below the first container */   top: 100vh; }

At this point, the effect should be taking shape. You can see that scrolling creates an illusion where the knockout text appears to change backgrounds. Really, it is just our clipping mask revealing a different <h1> element depending on which parent container overlaps the center of the screen.

Let’s make Safari happy

If you are using Safari, you may have noticed that its render engine is not refreshing the view properly when scrolling. Add the following code to the .container class to force it to refresh correctly.

.container {   /* ... */    /* Safari hack */   -webkit-mask-image: -webkit-linear-gradient(top, #ffffff 0%,#ffffff 100%); }

Here’s the complete code up to this point.

Time to clean house

Let’s make sure our HTML is following accessibility best practices. Users not using assistive tech can’t tell that there are two identical <h1> elements in our document, but those using a screen reader sure will because both headings are announced. Let’s add aria-hidden to our second container to let screen readers know it is purely decorative.

<!-- Second container --> <div class="container container_image" aria-hidden="true">   <div class="title_wrapper">     <h1>The Great Outdoors</h1>   </div> </div>

Now, the world is our oyster when it comes to styling. We are free to modify the fonts and font sizes to make the text just how we want. We could even take this further by adding a parallax effect or replacing the background image with a video. But, hey, at that point, just be sure to put a little additional work into the accessibility so those who prefer less motion get the right experience.

That wasn’t so hard, was it?


The post Going From Solid to Knockout Text on Scroll appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

Old is Solid; New Gets Talked About

When Chris asked me to write about “one thing I learned about building websites this year” I admit my brain immediately went through a list of techniques and CSS properties I started using this year. But then I paused. Other people can write about that much better than I can. What’s something that I specifically have learned?

Then I realized that I’ve been “learning” the same lesson for the last five years, yet I keep falling into the same trap time and time again. I always think that far more people are using the latest, coolest technology out there than there really are.

I think most of you feel the same. If you follow Twitter or any web development blog, it’s almost like everyone is using the latest and the greatest. And the latest and the greatest also seems to change weekly, if not daily. “What’s your favorite React state library? Well it’s Redux, no wait MobX, no wait Unstated, no wait Recoil, no wait, Jotai, no wait, Valtio, no wait…” The constant change can be exhausting, like you’re always falling behind compared to your peers.

But that’s not remotely the case. The vast majority of web developers use “boring” or “old” technology. That makes sense intuitively: most of the stuff on the web today was built …before today. When that needs to be maintained, it’s in the technologies that were in use when it was built. And herein lies the crux: We all maintain old things more than we build new things.

“The best bet for 2030” by CommitStrip

So you feel like everyone else gets to play with cool stuff like “auto-reloading serverless static deploys” while you’re still updating your Grunt configuration. Trust me, there are way more people updating their Grunt configuration right now than doing a serverless static deploy (whatever that may be).

That web dev you admire that’s all-in on Tailwind 2.0? They’re still maintaining a Bootstrap 2.3 website. That JavaScript guru that switches state libraries every week? They’re still maintaining a huge application using Flow. New just gets talked about more often.

I could mention the percentage of websites that run WordPress versus the percentage of sites that run React, but that’s not really the point. If you spend time in the web dev community, it feels like one is old-hat and the other isn’t.

Old can be solid, it can be dependable and it can be predictable. There are times where it’s fun to try new stuff and tell people about it, and there’s times to reach` for the technology you know so you can get stuff done. 

My guess is that I’ll keep thinking, “Well no one really uses $ foo anymore,” well into 2021 and beyond—it’s such an automatic thought. But I have to keep reminding myself that it’s wrong. For whatever value of $ foo, there are tons of people still using it, and it’s still valuable.


The post Old is Solid; New Gets Talked About appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]