DevGang
Авторизоваться

Как был разработан сайт complexus  с помощью NextJS

В этой статье в первую очередь будет озвучено об инструментах, которые можно использовать, и о том, как их использовали для создания динамического веб-сайта complexus, которая объединила эстетику и функциональность.. Это инструменты, которые обычно используются для проектов наших клиентов.

Дизайн

Команда дизайнеров использовала figma для создания дизайна сайта, скриншот начальной версии показан ниже. Дизайн был завершен после четырех итераций, первой из которых был низкоточный каркас. Почти каждый в той или иной степени внес свой вклад в разработку.

Содержание

У нас был человек, ответственный за копирайтинг материалов сайта, и они сотрудничали с командой дизайнеров, чтобы убедиться, что то, что они пишут, соответствует дизайну.

Разработка

Мы приступили к созданию веб-сайта после создания дизайна и контента. Позвольте мне перечислить некоторые вещи, на которых работает веб-сайт, прежде чем я перейду к техническим аспектам.

  1. Реагировать на пользовательский интерфейс
  2. NextJS для SSG/SSR/ISSG
  3. Tailwind css для стилизации
  4. Стилизация модулей CSS без коллизии имен классов
  5. JavaScript-анимации GSAP
  6. Mailchimp для списка рассылки
  7. Vercel CI/CD и хостинг
  8. Анимация JavaScript движения Framer
  9. TypeScript для добавления безопасности типов в нашу кодовую базу

Я, вероятно, потрачу немного больше времени на этот этап, потому что разработка была самой увлекательной фазой. Reactjs — это основа веб-сайта, и все компоненты были построены на ней. Ниже приведен пример повторно используемого Button компонента и его стилей.

Кнопка.tsx

import {
  FC,
  forwardRef,
  ButtonHTMLAttributes,
  JSXElementConstructor,
  ReactElement,
} from 'react';
import Link from 'next/link';
import { motion } from 'framer-motion';
import cn from 'classnames';

import s from './Button.module.scss';

interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  href?: string;
  className?: string;
  disabled?: boolean;
  rightIcon?: ReactElement;
  leftIcon?: ReactElement;
  active?: boolean;
  target?: '_blank' | '_self' | '_parent' | '_top';
  size?: 'sm' | 'md' | 'lg';
  variant?:
    | 'primary'
    | 'secondary'
    | 'white'
    | 'black'
    | 'naked'
    | 'outlineBlack'
    | 'outlineWhite'
    | 'square';
  as?: 'button' | 'a' | JSXElementConstructor<any>;
}

export const Button: FC<ButtonProps> = forwardRef((props, buttonRef) => {
  const {
    as: Tag = 'button',
    variant = 'primary',
    size = 'md',
    target = '_self',
    href,
    active,
    rightIcon,
    leftIcon,
    className,
    disabled,
    children,
    ...rest
  } = props;
  const classes = cn(
    s.root,
    {
      [s.primary]: variant === 'primary',
      [s.outlineBlack]: variant === 'outlineBlack',
      [s.outlineWhite]: variant === 'outlineWhite',
      [s.secondary]: variant === 'secondary',
      [s.white]: variant === 'white',
      [s.naked]: variant === 'naked',
      [s.black]: variant === 'black',
      [s.square]: variant === 'square',
      [s.md]: size === 'md',
      [s.sm]: size === 'sm',
      [s.lg]: size === 'lg',
      'flex items-center gap-3': (rightIcon || leftIcon) && size === 'md',
      [s.dFlex]: (rightIcon || leftIcon) && size === 'lg',
      [s.active]: active,
    },
    className
  );

  return (
    <motion.span
      className='inline-block'
      initial={{ y: 10, opacity: 0 }}
      transition={{
        duration: 1,
        type: 'spring',
        damping: 5,
      }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true, amount: 0.5 }}
    >
      {href ? (
        <Tag {...rest}>
          <Link href={href}>
            <a className={classes} target={target}>
              {leftIcon}
              <span>{children}</span>
              {rightIcon}
            </a>
          </Link>
        </Tag>
      ) : (
        <Tag disabled={disabled} className={classes} {...rest}>
          {leftIcon}
          <span>{children}</span>
          {rightIcon}
        </Tag>
      )}
    </motion.span>
  );
});

Button.displayName = 'Button';

Button.module.scss

.root {
  @apply mb-1 transition duration-200 ease-linear;
}

.dFlex {
  @apply flex items-center gap-5;
}

.primary {
  @apply relative overflow-hidden border border-primary bg-primary text-white;

  span,
  svg {
    @apply relative z-[2];
  }

  &::after {
    content: '';
    @apply absolute top-0 left-0 block h-full w-0 bg-white transition-[width] duration-300 ease-linear;
  }

  &:hover {
    @apply text-primary;
  }

  &:hover::after {
    @apply w-full;
  }
}

.naked {
  @apply bg-transparent;
}

.black {
  @apply relative border border-black bg-black text-white;

  &::after {
    content: '';
    @apply absolute top-0 left-0 block h-full w-0 bg-white mix-blend-difference transition-[width] duration-300 ease-linear;
  }
  &:hover::after {
    @apply w-full;
  }
}

.white {
  @apply border border-white bg-white text-primary;
}

.secondary {
  @apply border border-secondary bg-secondary text-white;
}

.outlineBlack {
  @apply relative border border-black;

  &::after {
    content: '';
    @apply absolute top-0 left-0 block h-full w-0 bg-white mix-blend-difference transition-[width] duration-300 ease-linear;
  }
  &:hover::after {
    @apply w-full;
  }
}

.square {
  @apply relative flex h-12 w-12 items-center justify-center rounded-full border border-black p-0 hover:scale-110 3xl:h-20 3xl:w-20 3xl:border-2 #{!important};
}

.outlineWhite {
  @apply relative border border-white text-white;

  &::before {
    content: '';
    @apply absolute left-[20%] -bottom-[1px] h-[1px] w-7 bg-black transition-[left];
  }

  &::after {
    content: '';
    @apply absolute right-[20%] -top-[1px] h-[1px] w-7 bg-black transition-[right];
  }

  &:hover::before {
    @apply left-[30%];
  }
  &:hover::after {
    @apply right-[30%];
  }

  &:hover {
    @apply opacity-100;
  }
}

.sm {
  @apply px-4 py-2;
}

.md {
  @apply px-4 py-[10px] xl:px-6;
}

.lg {
  @apply px-5 py-4 text-sm font-medium xl:py-[1.1rem] xl:px-8 xl:text-base 3xl:px-16 3xl:py-6 3xl:text-lg;
}

Компоненты были закодированы с использованием TypeScript для безопасности типов. TypeScript также помогает в написании самодокументируемого кода. Для стилизации было выбрано tailwindcss, но обратите внимание, что классы в наших компонентах реакции чистые, потому что служебные классы попутного ветра находятся в отдельном файле css, который представляет собой файл CSS module. Модули CSS помогают избежать конфликта пространств имен для классов CSS. Ниже будет показано, как Button можно использовать.

<Button
  variant='black'
  size='lg'
  href='/contact'
  className='relative ml-2'
  rightIcon={<Line />}
>
  Get in touch
</Button>

Все наши многоразовые компоненты закодированы таким образом.

Эти небольшие компоненты, такие как ButtonLink, Box и Text, расположены в папке components/ui/ и экспортируются с использованием одного файла index.ts, чтобы их можно было импортировать следующим образом.

import { Text, Box, Container, Link, Button } from '@components/ui';

Общие элементы, такие как Navigation и Footer, находятся в папке components/shared/,  а элементы, характерные для страницы, находятся в папке components/pages/[page-name].

SEO

Нам нужно было убедиться, что наш веб-сайт дружелюбен к поисковым системам, поэтому мы создали Page компонент, который использует некоторые SEO-данные в качестве реквизита, и каждая страница на сайте использует его в качестве родителя.

Страница.tsx

import { FC } from 'react';
import Head from 'next/head';

interface Props {
  title: string;
  description: string;
  image: string;
  canonicalURL?: string;
}

export const Page: FC<Props> = ({
  children,
  title,
  description,
  image,
  canonicalURL,
}) => {
  return (
    <>
      <Head>
        <title>{title}</title>
        <meta name='description' content={description} />
        <meta name='author' content='Complexus Technologies' />
        <meta name='image' content={image} />
        <meta name='og:title' content={title} />
        <meta name='og:description' content={description} />
        <meta name='og:image' content={image} />
        <meta name='og:url' content='https://complexus.tech' />
        <meta name='og:site_name' content='Complexus Technologies' />
        <meta name='og:type' content='website' />
        <meta name='twitter:card' content='summary_large_image' />
        <meta name='twitter:title' content={title} />
        <meta name='twitter:alt' content={title} />
        <meta name='twitter:description' content={description} />
        <meta name='twitter:image' content={image} />
        <meta name='theme-color' content='#f43f5e' />
        <meta name='twitter:site' content='@complexus_tech' />
        <meta name='twitter:creator' content='@complexus_tech' />
        {canonicalURL && <link rel='canonical' href={canonicalURL} />}
      </Head>
      <main>{children}</main>
    </>
  );
};

Пример использования страницы

import type { NextPage } from 'next';
import { Page, HowMuch } from '@components/shared';
import { Hero, Intro, Team } from '@components/pages/about';

const Home: NextPage = () => {
  return (
    <Page
      title='About | Complexus Technologies'
      description='Complexus helps you achieve your business goals through effective planning, design, and development.'
      image='https://complexus.tech/images/banner.jpg'
      url='https://complexus.tech/about/'
      canonicalURL='https://complexus.tech/about/'
      keywords='complexus, complexus technologies, complex, zimbabwe sofware company, mobile development, it consultancy'
    >
      <Hero />
      <Intro />
      <Team />
      <HowMuch />
    </Page>
  );
};

export default Home;

Мы также использовали другие инструменты, такие как Google Analytics, Google My Business и консоль поиска Google для SEO.
Также были использованы некоторые теги открытого графа, чтобы убедиться, что у сайта есть хорошие права доступа, когда мы делимся им в социальных сетях.

Чтобы делиться информацией о событиях в Complexus, мы запускаем список рассылки с помощью Mailchimp.

#JavaScript #NestJS
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться