Глава 1. Пошаговое руководство по созданию шаблона электронной коммерции React с использованием Tailwind CSS.

Меня вдохновляет шаблон списка продуктов электронной коммерции Nordic Store. Этот шаблон использует Tailwind CSS и был создан Tailwind Toolbox.

Шаблон Nordic Store — это статический CSS-шаблон Tailwind. Поэтому я планировал создать динамический шаблон Nordic Store с помощью React.

В этой серии блогов мы собираемся создать динамический шаблон электронной коммерции с использованием React и Tailwind CSS.

Зачем использовать Tailwind CSS

Tailwind — это ориентированная на утилиты CSS-инфраструктура для быстрого создания настраиваемых пользовательских интерфейсов. Просто служебные классы помогают вам стилизовать веб-приложения. Таким образом, мы можем сделать все ваши стили с помощью служебных классов и без сенсорных таблиц стилей.

Также готовые компоненты доступны на Tailwind UI.

Глава 1: Начало

В этой главе мы собираемся выполнить следующие задачи:

  • Создайте новое приложение React
  • Установка Tailwind CSS и React Router
  • Установка зависимостей
  • Создание базового компонента макета
  • Создание домашней страницы
  • Создание статической домашней страницы Nordic Store

Создайте новое приложение React

Создайте свое новое приложение React, используя create-react-app

Terminal

npx create-react-app tailwindcss-nordic-store
cd tailwindcss-nordic-store

Установка Tailwind CSS и React Router

Установка Tailwind CSS объясняется в документе Tailwind CSS здесь

Сначала установите tailwindcss, postcss и autoprefixer, а затем запустите команду инициализации, чтобы сгенерировать tailwind.config.js и postcss.config.js.

Terminal

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Откройте файл tailwind.config.js и добавьте все пути к файлам шаблона.

tailwind.config.js

module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Добавьте директивы @tailwind для каждого из слоев Tailwind в ваш файл src/index.css.

src/index.css

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

Мы успешно установили Tailwind CSS в ваше приложение. Для динамических шаблонов нам нужны разные страницы, поэтому мы собираемся установить React Router в ваше приложение.

Terminal

npm install react-router-dom@6

Установка зависимостей

Безголовый пользовательский интерфейс для управления всем интерактивным поведением и Heroicons для значков, поэтому вам нужно будет добавить эти две библиотеки в свое приложение.

Terminal

npm install @headlessui/react @heroicons/react

Создание базового компонента макета

Основные этапы создания макета опубликованы в моем предыдущем блоге Создайте свой собственный компонент макета в React.



Загрузите приложение и скопируйте папку компонентов в свой https://github.com/balajidharma/react-basic-layout/archive/refs/heads/master.zip.

Обновите свой App.js и включите наш макет в ваш index.js:

src/App.js

import './App.css';function App() {
  return (
    <div className="App">
      Hello world!
    </div>
  );
}export default App;

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import Layout from './components/Layout/Layout';ReactDOM.render(
  <Layout>
    <App />
  </Layout>,
  document.getElementById('root')
);

Теперь запустите свое приложение и просмотрите основные разделы «Заголовок», «Нижний колонтитул» и «Содержимое» браузера.

Terminal

npm start

Browser

Создание домашней страницы

Пришло время создать нашу страницу с помощью React Router. Создайте новую папку pages в папке src. Создайте компонент Home внутри папки pages

src/pages/Home.js

import React from 'react';
class Home extends React.Component {
  render() {
    return (
      <div>
        Home page
      </div>
    );
  }
}
export default Home;

Компонент React class, используемый для нашего приложения. Подробнее о компонентах React здесь.

Также прочтите Различия между функциональными компонентами и классовыми компонентами в React.

Включите React Router и домашнюю страницу в виде файла index.js. Также мы собираемся удалить app.js.

src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Routes, Route } from "react-router-dom";
import './index.css';
import Layout from './components/Layout/Layout';
import Home from './pages/Home';
ReactDOM.render(
  <BrowserRouter>
    <Layout>
      <Routes>
        <Route path="/" element={<Home/>} />
      </Routes>
    </Layout>
  </BrowserRouter>,
  document.getElementById('root')
);

Создание статической домашней страницы Nordic Store

Мы почти завершили базовую настройку. Мы разделили шаблон Nordic Store на разделы Заголовок, Нижний колонтитул и Главная в вашем приложении.

src/components/Layout/Header/Header.js

import React from "react";
class Header extends React.Component {
  render() {
    return (
      <header>
        <nav id="header" className="w-full z-30 top-0 py-1">
          <div className="w-full container mx-auto flex flex-wrap items-center justify-between mt-0 px-6 py-3">
<label htmlFor="menu-toggle" className="cursor-pointer md:hidden block">
              <svg className="fill-current text-gray-900" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20">
                <title>menu</title>
                <path d="M0 3h20v2H0V3zm0 6h20v2H0V9zm0 6h20v2H0v-2z"></path>
              </svg>
            </label>
            <input className="hidden" type="checkbox" id="menu-toggle" />
<div className="hidden md:flex md:items-center md:w-auto w-full order-3 md:order-1" id="menu">
              <nav>
                <ul className="md:flex items-center justify-between text-base text-gray-700 pt-4 md:pt-0">
                  <li><a className="inline-block no-underline hover:text-black hover:underline py-2 px-4" href="#">Shop</a></li>
                  <li><a className="inline-block no-underline hover:text-black hover:underline py-2 px-4" href="#">About</a></li>
                </ul>
              </nav>
            </div>
<div className="order-1 md:order-2">
              <a className="flex items-center tracking-wide no-underline hover:no-underline font-bold text-gray-800 text-xl " href="#">
                <svg className="fill-current text-gray-800 mr-2" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
                  <path d="M5,22h14c1.103,0,2-0.897,2-2V9c0-0.553-0.447-1-1-1h-3V7c0-2.757-2.243-5-5-5S7,4.243,7,7v1H4C3.447,8,3,8.447,3,9v11 C3,21.103,3.897,22,5,22z M9,7c0-1.654,1.346-3,3-3s3,1.346,3,3v1H9V7z M5,10h2v2h2v-2h6v2h2v-2h2l0.002,10H5V10z" />
                </svg>
                NORDICS
              </a>
            </div>
<div className="order-2 md:order-3 flex items-center" id="nav-content">
<a className="inline-block no-underline hover:text-black" href="#">
                <svg className="fill-current hover:text-black" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
                  <circle fill="none" cx="12" cy="7" r="3" />
                  <path d="M12 2C9.243 2 7 4.243 7 7s2.243 5 5 5 5-2.243 5-5S14.757 2 12 2zM12 10c-1.654 0-3-1.346-3-3s1.346-3 3-3 3 1.346 3 3S13.654 10 12 10zM21 21v-1c0-3.859-3.141-7-7-7h-4c-3.86 0-7 3.141-7 7v1h2v-1c0-2.757 2.243-5 5-5h4c2.757 0 5 2.243 5 5v1H21z" />
                </svg>
              </a>
<a className="pl-3 inline-block no-underline hover:text-black" href="#">
                <svg className="fill-current hover:text-black" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
                  <path d="M21,7H7.462L5.91,3.586C5.748,3.229,5.392,3,5,3H2v2h2.356L9.09,15.414C9.252,15.771,9.608,16,10,16h8 c0.4,0,0.762-0.238,0.919-0.606l3-7c0.133-0.309,0.101-0.663-0.084-0.944C21.649,7.169,21.336,7,21,7z M17.341,14h-6.697L8.371,9 h11.112L17.341,14z" />
                  <circle cx="10.5" cy="18.5" r="1.5" />
                  <circle cx="17.5" cy="18.5" r="1.5" />
                </svg>
              </a>
</div>
          </div>
        </nav>
      </header>
    );
  }
}
export default Header;

Не забудьте при копировании изменить class на className и for на htmlFor. Кроме того, игнорируйте действительные предупреждения href warnings:

src/components/Layout/Footer/Footer.js

import React from "react";
class Footer extends React.Component {
  render() {
    return (
      <footer className="container mx-auto bg-white py-8 border-t border-gray-400">
        <div className="container flex px-3 py-8 ">
          <div className="w-full mx-auto flex flex-wrap">
            <div className="flex w-full lg:w-1/2 ">
              <div className="px-3 md:px-0">
                <h3 className="font-bold text-gray-900">About</h3>
                <p className="py-4">
                  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vel mi ut felis tempus commodo nec id erat. Suspendisse consectetur dapibus velit ut lacinia.
                </p>
              </div>
            </div>
            <div className="flex w-full lg:w-1/2 lg:justify-end lg:text-right">
              <div className="px-3 md:px-0">
                <h3 className="font-bold text-gray-900">Social</h3>
                <ul className="list-reset items-center pt-3">
                  <li>
                    <a className="inline-block no-underline hover:text-black hover:underline py-1" href="#">Add social links</a>
                  </li>
                </ul>
              </div>
            </div>
          </div>
        </div>
      </footer>
    );
  }
}
export default Footer;

src/pages/Home.js

import React from 'react';
const products = [
  {
    id: 1,
    name: 'Product Name',
    href: '#',
    price: '$9.99',
    imageSrc: 'https://images.unsplash.com/photo-1555982105-d25af4182e4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&h=400&q=80',
  },
  {
    id: 2,
    name: 'Product Name',
    href: '#',
    price: '$10.99',
    imageSrc: 'https://images.unsplash.com/photo-1508423134147-addf71308178?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&h=400&q=80',
  },
  {
    id: 3,
    name: 'Product Name',
    href: '#',
    price: '$12.99',
    imageSrc: 'https://images.unsplash.com/photo-1449247709967-d4461a6a6103?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&h=400&q=80',
  },
  {
    id: 4,
    name: 'Product Name',
    href: '#',
    price: '$9.99',
    imageSrc: 'https://images.unsplash.com/reserve/LJIZlzHgQ7WPSh5KVTCB_Typewriter.jpg?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&h=400&q=80',
  },
  {
    id: 5,
    name: 'Product Name',
    href: '#',
    price: '$6.99',
    imageSrc: 'https://images.unsplash.com/photo-1467949576168-6ce8e2df4e13?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&h=400&q=80',
  },
  {
    id: 6,
    name: 'Product Name',
    href: '#',
    price: '$10.99',
    imageSrc: 'https://images.unsplash.com/photo-1544787219-7f47ccb76574?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&h=400&q=80',
  },
  {
    id: 7,
    name: 'Product Name',
    href: '#',
    price: '$22.99',
    imageSrc: 'https://images.unsplash.com/photo-1550837368-6594235de85c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=400&h=400&q=80',
  },
  {
    id: 8,
    name: 'Product Name',
    href: '#',
    price: '$19.99',
    imageSrc: 'https://images.unsplash.com/photo-1551431009-a802eeec77b1?ixlib=rb-1.2.1&auto=format&fit=crop&w=400&h=400&q=80',
  },
]
class Home extends React.Component {
  render() {
    return (
      <div>
<section className="bg-white py-8">
<div className="container mx-auto flex items-center flex-wrap pt-4 pb-12">
<nav id="store" className="w-full z-30 top-0 px-6 py-1">
              <div className="w-full container mx-auto flex flex-wrap items-center justify-between mt-0 px-2 py-3">
<a className="uppercase tracking-wide no-underline hover:no-underline font-bold text-gray-800 text-xl " href="#">
                  Store
                </a>
<div className="flex items-center" id="store-nav-content">
<a className="pl-3 inline-block no-underline hover:text-black" href="#">
                    <svg className="fill-current hover:text-black" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
                      <path d="M7 11H17V13H7zM4 7H20V9H4zM10 15H14V17H10z" />
                    </svg>
                  </a>
<a className="pl-3 inline-block no-underline hover:text-black" href="#">
                    <svg className="fill-current hover:text-black" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
                      <path d="M10,18c1.846,0,3.543-0.635,4.897-1.688l4.396,4.396l1.414-1.414l-4.396-4.396C17.365,13.543,18,11.846,18,10 c0-4.411-3.589-8-8-8s-8,3.589-8,8S5.589,18,10,18z M10,4c3.309,0,6,2.691,6,6s-2.691,6-6,6s-6-2.691-6-6S6.691,4,10,4z" />
                    </svg>
                  </a>
</div>
              </div>
            </nav>
<div className="grid grid-cols-1 gap-y-10 sm:grid-cols-2 gap-x-6 lg:grid-cols-3 xl:grid-cols-4 xl:gap-x-8">
              {products.map((product) => (
                <a key={product.id} href={product.href}>
                  <img className="hover:grow hover:shadow-lg" src={product.imageSrc} />
                  <div className="pt-3 flex items-center justify-between">
                    <p className="">{product.name}</p>
                    <svg className="h-6 w-6 fill-current text-gray-500 hover:text-black" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                      <path d="M12,4.595c-1.104-1.006-2.512-1.558-3.996-1.558c-1.578,0-3.072,0.623-4.213,1.758c-2.353,2.363-2.352,6.059,0.002,8.412 l7.332,7.332c0.17,0.299,0.498,0.492,0.875,0.492c0.322,0,0.609-0.163,0.792-0.409l7.415-7.415 c2.354-2.354,2.354-6.049-0.002-8.416c-1.137-1.131-2.631-1.754-4.209-1.754C14.513,3.037,13.104,3.589,12,4.595z M18.791,6.205 c1.563,1.571,1.564,4.025,0.002,5.588L12,18.586l-6.793-6.793C3.645,10.23,3.646,7.776,5.205,6.209 c0.76-0.756,1.754-1.172,2.799-1.172s2.035,0.416,2.789,1.17l0.5,0.5c0.391,0.391,1.023,0.391,1.414,0l0.5-0.5 C14.719,4.698,17.281,4.702,18.791,6.205z" />
                    </svg>
                  </div>
                  <p className="pt-1 text-gray-900">{product.price}</p>
                </a>
              ))}
            </div>
</div>
</section>
</div>
    );
  }
}
export default Home;

Мы создали домашнюю страницу без карусели. Мы реализуем карусель в следующей главе.

Добавьте приведенный ниже стиль для мобильного меню.

src/index.css

@tailwind base;
@tailwind components;
@tailwind utilities;
#menu-toggle:checked + #menu {
  display: block;
}

Полный рабочий код этой главы выложен и доступен на Github — archive/Chapter_1.

Полная демонстрация:https://balajidharma.github.io/tailwindcss-nordic-store/

Далее: Глава 2. Пошаговое руководство по использованию RESTful API в React

На этом мы заканчиваем нашу первую главу. Спасибо за чтение.

Оставайтесь с нами, чтобы узнать больше!

Больше контента на plainenglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Получите эксклюзивный доступ к возможностям написания и советам в нашем сообществе Discord.