DEV Community

Cover image for SolidJS and Tailwindcss Todo App
Harsh Mangalam
Harsh Mangalam

Posted on

SolidJS and Tailwindcss Todo App

Solidjs is a new Javascript Framework which introduce the clear concept of fine-grained reactivity system.

Every Component executes once and it is the Hooks and bindings that execute many times as their dependencies update.

In this post i will show how we can combine tailwindcss and solidjs to create simple todo.

For icons i have used heroicons

Todo App

Todo App

create new solid js application using vite



npx degit solidjs/templates/js todo

cd todo

pnpm i



Enter fullscreen mode Exit fullscreen mode

setup tailwindcss



npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

npx tailwindcss init




Enter fullscreen mode Exit fullscreen mode

src/styles/index.css




@tailwind base;
@tailwind components;
@tailwind utilities;



Enter fullscreen mode Exit fullscreen mode

tailwind.config.js




const { rule } = require("postcss");

module.exports = {
  purge: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
  darkMode: false, // or 'media' or 'class',
  theme: {
    extend: {},
  },
  variants: {
    extend: {},
  },
  plugins: [],
};




Enter fullscreen mode Exit fullscreen mode

src/index.jsx




import { render } from "solid-js/web";

import "./styles/index.css";
import App from "./App";

render(App, document.getElementById("root"));





Enter fullscreen mode Exit fullscreen mode

src/components/Button.jsx




function Button(props) {
  return (
    <button
      onClick={props.onClick}
      class={`bg-${props.bg}-500 text-${props.bg}-50 hover:bg-${props.bg}-700 focus:outline-none focus:ring-2 focus:ring-${props.bg}-600 focus:ring-opacity-50 px-1 py-1 md:px-4 md:py-2 rounded-lg ${props.class}`}
    >
      {props.children}
    </button>
  );
}

export default Button;



Enter fullscreen mode Exit fullscreen mode

src/components/InputField.jsx




function InputField(props) {
  return (
    <input
      type="text"
      class={`border-2 border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-600 px-1 py-1  md:px-2 md:py-2 rounded-lg ${props.class}`}
      onInput={props.onInput}
      value={props.value}
    />
  );
}

export default InputField;



Enter fullscreen mode Exit fullscreen mode

src/components/CheckBox.jsx




function CheckBox(props) {
  return (
    <input
      type="checkbox"
      checked={props.checked}
      onChange={props.onChange}
      class="w-4 h-4 md:w-6 md:h-6 flex-shrink-0"
    />
  );
}

export default CheckBox;



Enter fullscreen mode Exit fullscreen mode

src/components/Navbar.jsx




function Navbar() {
  return (
    <nav className="flex items-center justify-between px-2 py-4 bg-gray-100">
      <div>
        <h6 class="text-2xl font-bold">SolidJs Todo App</h6>
      </div>
      <ul>
          <li>
            <a
              href="https://github.com/harshmangalam/solidjs-tailwindcss-todo"
              class="flex items-center space-x-2 bg-black text-white rounded-full p-1 md:px-4 md:py-2"
            >
              <svg fill="currentColor" class="h-6 w-6 text-white ">
                <path
                  fill-rule="evenodd"
                  clip-rule="evenodd"
                  d="M12 2C6.477 2 2 6.463 2 11.97c0 4.404 2.865 8.14 6.839 9.458.5.092.682-.216.682-.48 0-.236-.008-.864-.013-1.695-2.782.602-3.369-1.337-3.369-1.337-.454-1.151-1.11-1.458-1.11-1.458-.908-.618.069-.606.069-.606 1.003.07 1.531 1.027 1.531 1.027.892 1.524 2.341 1.084 2.91.828.092-.643.35-1.083.636-1.332-2.22-.251-4.555-1.107-4.555-4.927 0-1.088.39-1.979 1.029-2.675-.103-.252-.446-1.266.098-2.638 0 0 .84-.268 2.75 1.022A9.606 9.606 0 0112 6.82c.85.004 1.705.114 2.504.336 1.909-1.29 2.747-1.022 2.747-1.022.546 1.372.202 2.386.1 2.638.64.696 1.028 1.587 1.028 2.675 0 3.83-2.339 4.673-4.566 4.92.359.307.678.915.678 1.846 0 1.332-.012 2.407-.012 2.734 0 .267.18.577.688.48C19.137 20.107 22 16.373 22 11.969 22 6.463 17.522 2 12 2z"
                ></path>
              </svg>
              <span class="hidden md:block">Github</span>
            </a>
          </li>
        </ul>
    </nav>
  );
}

export default Navbar;



Enter fullscreen mode Exit fullscreen mode

src/App.jsx





import { createEffect, createSignal, For } from "solid-js";
import { createStore } from "solid-js/store";
import Navbar from "./components/Navbar";
import InputField from "./components/InputField";
import Button from "./components/Button";
import CheckBox from "./components/CheckBox";

function App() {
  const [store, setStore] = createStore({
    todos: [],
  });

  const [text, setText] = createSignal("");

  createEffect(() =>
    console.log(
      store.todos.map((s) => ({ text: s.text, completed: s.completed }))
    )
  );

  return (
    <div class="min-h-screen flex flex-col justify-between space-y-4">
      <header>
        <Navbar />
      </header>
      <main class="container md:max-w-lg mx-auto flex-grow">
        <section class=" flex items-center space-x-2 bg-purple-100 px-2 md:px-4 py-6">
          <InputField
            value={text()}
            onInput={(e) => setText(e.currentTarget.value)}
          />
          <Button
            bg="purple"
            class="flex items-center space-x-1"
            onClick={() => {
              setStore({
                todos: [
                  ...store.todos,
                  {
                    text: text(),
                    completed: false,
                  },
                ],
              });
              setText("");
            }}
          >
            <svg
              xmlns="http://www.w3.org/2000/svg"
              class="h-5 w-5"
              fill="none"
              viewBox="0 0 24 24"
              stroke="currentColor"
            >
              <path
                stroke-linecap="round"
                stroke-linejoin="round"
                stroke-width="2"
                d="M12 6v6m0 0v6m0-6h6m-6 0H6"
              />
            </svg>
            <span>Add</span>
          </Button>
        </section>

        <section class="py-4 grid grid-cols-1 gap-2">
          <For each={store.todos}>
            {(todo, i) => {
              const { completed, text } = todo;

              return (
                <div class="flex items-center space-x-2 bg-green-100 px-2 md:px-4 py-4">
                  <CheckBox
                    checked={completed}
                    onChange={(e) =>
                      setStore("todos", i(), { completed: e.target.checked })
                    }
                  />
                  <InputField
                    value={text}
                    onInput={(e) =>
                      setStore("todos", i(), { text: e.currentTarget.value })
                    }
                  />
                  <Button
                    bg="red"
                    class="flex items-center space-x-1"
                    onClick={() =>
                      setStore("todos", (t) => [
                        ...t.slice(0, i()),
                        ...t.slice(i() + 1),
                      ])
                    }
                  >
                    <svg
                      xmlns="http://www.w3.org/2000/svg"
                      class="h-6 w-6 text-white"
                      fill="none"
                      viewBox="0 0 24 24"
                      stroke="currentColor"
                    >
                      <path
                        stroke-linecap="round"
                        stroke-linejoin="round"
                        stroke-width="2"
                        d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
                      />
                    </svg>
                  </Button>
                </div>
              );
            }}
          </For>
        </section>
      </main>
      <footer class="py-2 bg-gray-100">
        <p class="text-center">SolidJs Todo - {new Date().getFullYear()}</p>
      </footer>
    </div>
  );
}

export default App;



Enter fullscreen mode Exit fullscreen mode

github repo here

Top comments (0)