Cómo construir un sitio estático de WordPress con Frontity

En los últimos años, hemos explorado un montón de pilas y frameworks de moda. Todos ellos han aportado mejoras de rendimiento y facilidad de uso, pero hace tiempo que no hablamos del OG de la web moderna. Estoy hablando de WordPress, por supuesto.

Aunque WordPress impulsa el 42% de todos los sitios web de Internet, a veces da la sensación de que WordPress no ha evolucionado lo suficiente como para competir con nuevos frameworks como Next.js, Vue.js, Gatsby y otros.

¿Y si te dijera que es posible seguir usando WordPress mientras aprovechas la potencia y funcionalidad de React para construir tu frontend? Pues bien, gracias a Frontity, construir rápidamente un sitio web de WordPress de última generación es accesible.

Exploremos este nuevo framework y veamos cómo podemos aprovecharlo para construir una web de comercio electrónico.



¿Qué es Frontity?

Frontity es un framework de código abierto basado en React. Utiliza tu sitio de WordPress como un CMS sin cabeza y lo renderiza en un framework de React. Te permite construir un rápido sitio web estático de WordPress sin cabeza rápidamente.

Funciona como un generador de sitios estáticos compilando y construyendo las páginas HTML y sirviéndolas cuando hay una solicitud del cliente. El pre-renderizado sin servidor de React se encarga del contenido dinámico de la página como lo haría cualquier otro sitio estático.

Frontity gestiona el bundling, el transpiling, el routing, el server rendering, la gestión de estados, la gestión de CSS, la recuperación de datos de WordPress, etc. Es un marco de trabajo de configuración cero que soporta TypeScript así como Emotion para el estilo CSS en JavaScript. También es compatible con Google AMP con el mismo código base.

Este framework también ofrece la gestión del estado de la aplicación a través de su gestor de estados llamado Frontity Connect. El objeto JavaScript que contiene todos los estados expuestos por su paquete se fusiona con la configuración. De esta manera, los paquetes pueden acceder al estado expuesto por otros paquetes.

Desarrollar un sitio web moderno de WordPress es ahora una brisa. Y para alguien como yo, que no es desarrollador, me hace la vida más fácil.

Las características que trae Frontity son similares a las que obtendrías de otro generador de sitios estáticos.

Pero lo que hace que sea una opción de emparejamiento sin duda con WordPress es lo rápido que es. En serio. Como se renderiza del lado del servidor, todo es rápido y se carga casi al instante.

¿Cómo funciona?

Frontity utiliza el REST de WordPress API para obtener los datos de tu sitio de WordPress (servidor PHP) y los renderiza en tu frontend React (servidor Node.js). Frontity entonces renderiza las páginas en HTML o AMP. Frontity puede alojarse en un servidor Node.js normal o en un servicio sin servidor como AWS, Netlify o Vercel.

Eso está muy bien, pero ¿qué pasa con sus credenciales en la calle? Bueno, Frontity cuenta con la confianza de TikTok para construir su portal del creador, CNBC África y Forbes África, por nombrar algunos.



¿Cómo se compara con Gatsby?

Gatsby también ofrece un frontend para construir un sitio web estático utilizando una instancia de WordPress como un CMS sin cabeza. Así que vamos a explorar cómo Frontity se compara con él.

Para empezar, los dos frameworks están basados en React.

Tanto Gatsby como Frontity aportan tiempos de carga inicial de la página más rápidos gracias a las páginas estáticas. Estos frameworks también manejan el enrutamiento de la app out-of-the-box, por lo que no tenemos que configurarlo.

Ambos frameworks también se benefician de la división del código, que los optimiza para obtener mejores puntuaciones de rendimiento en Lighthouse.

Sin embargo, como hemos visto antes, Frontity es un framework listo para jugar en el que todo está preparado para nosotros. Es amigable para los desarrolladores (o, en mi caso, para los principiantes), no se necesita una configuración compleja, las herramientas como las consultas a las APIs están preconfiguradas.

Frontity también elimina la necesidad de lidiar con GraphQL proporcionando los datos a través de su gestor de estados.

Ahora basta de hablar; es hora de meterse de lleno en ello. Veamos por nosotros mismos lo que puede hacer este framework basado en React!



Tutorial: Construir un sitio de WordPress de comercio electrónico sin cabeza con Frontity

Para este tutorial, vamos a construir un simple sitio de comercio electrónico utilizando Snipcart para vender salsas picantes.



Requisitos previos



Paso 1: Crear nuestras páginas en WordPress

.El primer paso que tendremos que hacer es crear nuestras páginas en WordPress. Haremos un sitio de comercio electrónico sencillo con dos páginas (excluyendo las de nuestros productos). La primera será nuestra página de inicio y la otra una página sobre nosotros.

Para crearlas, basta con entrar en Páginas en el menú de administración de WordPress y hacer clic en ‘Añadir nuevo’ para crear nuestras páginas. Nuestra página de inicio se llamará ‘Hottest Hot Sauce Sauce’ y la página sobre nosotros se llamará ‘About Us’. Una vez hecho esto, asegúrate de ir a Configuración/Lectura y seleccionar <página estática=””> y seleccionar la página de inicio en el menú desplegable.</página>

Mientras la configuración, asegúrate de activar el nombre del post permalink en Configuración/Permalinks para que Frontity funcione correctamente.



Paso 2: Crear productos en WordPress

Ahora que tenemos una instancia de WordPress en funcionamiento con nuestras páginas creadas vamos a crear nuestros productos.

Advanced Custom Fields. Una vez instalado y activado. Vamos a crear un nuevo grupo de campos que contenga lo siguiente.

Después, selecciona ‘Entradas’ en el menú del tablero de WordPress y haz clic en ‘Añadir nuevo’.

Dale un nombre, y luego selecciona introducir los datos en los campos personalizados que acabamos de crear.

También necesitaremos añadir el plugin ACF a una API REST para poder obtener nuestros campos personalizados avanzados más adelante en Frontity.



Paso 3: Crear proyecto Frontity

Ahora que hemos configurado todo lo que necesitábamos en WordPress, es el momento de sumergirnos en Frontity.

npx frontity create my-first-frontity-project

La CLI de Frontity te pedirá que selecciones un tema. Para esta demostración, vamos a elegir @frontity/twentytwenty-theme.

Una vez hecho esto, tendrás todo lo necesario para empezar a desarrollar, y estaremos listos para el siguiente paso.



Paso 4: Conectar Frontity a la API REST de WordPress

.Para tener nuestros datos en Frontity, necesitaremos conectarnos a la API REST de WordPress. Para ello, abre frontity.settings.js y sustituyeTU-WORDPRESS_SITE.com (en el const settings) por la URL de nuestro sitio WordPress. Esto le dirá a Frontity dónde encontrar el contenido de nuestro sitio web.

En la misma constante, cambiaremos el valor de "title" y "description". Estos se renderizarán en la cabecera de nuestro sitio.

También necesitaremos conectar la API REST. Para ello, simplemente sustituye TU-STIO-WORDPRESS.com/wp-json por la URL de tu WordPress seguida de /wp-json.

Tenga en cuenta que esta ruta puede cambiar dependiendo de cómo haya configurado su página de inicio y de si está utilizando WordPress.org o WordPress.com.

También configuraremos el nombre del menú y sus rutas, el título del sitio y la descripción. <código>”título”</código> y <código>”descripción”</código> nos servirán para la meta de nuestro sitio.

const settings = {
  "name": "wordpress-frontity-snipcart",
  "state": {
    "frontity": {
      "url": "<https://snipcart-hotsauce-shop.azurewebsites.net/>",
      "title": "Snipcart Hot Sauce Shop",
      "description": "The Hottest Hot Sauce Shop!"
    }
  },
  "packages": [
    {
      "name": "@frontity/twentytwenty-theme",
      "state": {
        "theme": {
          "menu": [
            [
              "Shop",
              "/"
            ],
            [
              "About Us",
              "/about-us/"
            ]
          ],
          "featured": {
            "showOnList": false,
            "showOnPost": false
          }
        }
      }
    },
    {
      "name": "@frontity/wp-source",
      "state": {
        "source": {
          "api": "<https://snipcart-hotsauce-shop.azurewebsites.net/wp-json>"
        }
      }
    },
    "@frontity/tiny-router",
    "@frontity/html2react"
  ]
};

export default settings;

También necesitamos añadir connect Frontity a los datos de los campos personalizados y obtener la información de nuestros productos. Para ello, vamos a sustituir el contenido de packages/twentytwenty-theme/src/index.js por el siguiente:

import Theme from "./components";
import image from "@frontity/html2react/processors/image";
import link from "@frontity/html2react/processors/link";

// Custom handler for ACF options
const acfOptionsHandler = {
  pattern: "acf-options-page",
  func: async ({ route, state, libraries }) => {
    // 1. Get ACF option page from REST API.
    const response = await libraries.source.api.get({
      endpoint: `/acf/v3/posts`
    });
    const option = await response.json();

    // 2. Add data to `source`.
    const data = state.source.get(route);
    Object.assign(data, { ...option, isAcfOptionsPage: true });
  }
};

const twentyTwentyTheme = {
  name: "@frontity/twentytwenty-theme",
  roots: {
    /**
     *  In Frontity, any package can add React components to the site.
     *  We use roots for that, scoped to the `theme` namespace.
     */
    theme: Theme,
  },
  state: {
    /**
     * State is where the packages store their default settings and other
     * relevant state. It is scoped to the `theme` namespace.
     */
    theme: {
      colors: {
        gray: {
          base: "#6D6D6D",
          light: "#DCD7CA",
          lighter: "#F5EFE0",
        },
        primary: "#0aa7f5",
        headerBg: "#ffffff",
        footerBg: "#ffffff",
        bodyBg: "#f1f2f4",
      },
      // Whether to show the search button in page header
      showCartInHeader: true,
      // Menu links to display in the header
      menu: [],
      // State for the menu on mobile
      isMobileMenuOpen: false,
      // State for the search modal on mobile
      isSearchModalOpen: false,
      // Whether to show all post content or only excerpt (summary) in archive view
      showAllContentOnArchive: false,
      // Settings for the featured media (image or video)
      featuredMedia: {
        // Whether to show it on archive view
        showOnArchive: true,
        // Whether to show it on post
        showOnPost: true,
      },
      // Whether to auto-fetch links on a page. Values can be "no" | "all" | "in-view" | "hover"
      autoPrefetch: "in-view",

      /**
       * At the moment, we only include the ascii characters of Inter font.
       * Values can be "us-ascii" | "latin" | "all".
       */
      fontSets: "all",
    },
  },

  /**
   * Actions are functions that modify the state or deal with other parts of
   * Frontity like libraries.
   */
  actions: {
    theme: {
      beforeSSR: async ({ state, actions }) => {
        // This will make Frontity wait until the ACF options
        // page has been fetched and it is available
        // using state.source.get("acf-options-page").
        await actions.source.fetch("posts");
      },
      openMobileMenu: ({ state }) => {
        state.theme.isMobileMenuOpen = true;
      },
      closeMobileMenu: ({ state }) => {
        state.theme.isMobileMenuOpen = false;
      },
      openSearchModal: ({ state }) => {
        state.theme.isSearchModalOpen = true;
      },
      closeSearchModal: ({ state }) => {
        state.theme.isSearchModalOpen = false;
      },
    },
  },
  libraries: {
    source: {
      handlers: [acfOptionsHandler]
    },
    html2react: {
      /**
       * Add a processor to `html2react` so it processes the `<img>` tags
       * and internal link inside the content HTML.
       * You can add your own processors too.
       */
      processors: [image, link],
    },
  },
};

export default twentyTwentyTheme;
 

Deberíamos poder ver nuestro contenido de WordPress al construir nuestro proyecto. Ejecuta el comando build en la terminal:

npx frontify dev

Una vez construido, su navegador web debería lanzar automáticamente el host local. Si no es así, simplemente vaya a http://localhost:3000.



Paso 5: Instalar Snipcart

En packages/twentytwenty-theme/src/components/index.js añade las sugerencias de preconexión de Snipcart y la hoja de estilos en el elemento <head>:

<Head>
  //..
    <link rel="preconnect" href="<https://app.snipcart.com>"/>
    <link rel="preconnect" href="<https://cdn.snipcart.com>"/>
    <link rel="stylesheet" href="<https://cdn.snipcart.com/themes/v3.2.2/default/snipcart.css>" />
</Head>
 

En el mismo archivo, vamos a añadir Snipcart a nuestro sitio pegando estas dos líneas bajo el elemento <Footer />:

<script async src="<https://cdn. snipcart.com/themes/v3.2.2/default/snipcart.js>">/script>
< div hidden id="snipcart" data- api- clave="TU_PUBLICA_API_KEY"> </div>

Recordatorio de sustituir SU_PUBLIC_API_KEY por su clave pública de API. Puedes encontrarla en el Dashboard de Snipcart bajo el modo de prueba.

<p< blockquote="">



Paso 6: Crear un botón de compra & componente de tarjeta de producto Snipcart

.Ahora que hemos instalado Snipcart, es el momento de conectar los campos personalizados de nuestros productos que hemos creado anteriormente con el botón de compra de Snipcart. Al mismo tiempo, esto permitirá que Frontity muestre la información de los productos que hemos introducido en WordPress.

Para ello, crearemos una nueva carpeta en packages/twentytwenty-theme/src/components llamada ecommerce y crearemos dos nuevos archivos en ella. Uno llamado product-card.js y el otro llamado snipcart-button.js.

En product-card.js, vamos a crear un nuevo componente llamado ProductCard que recibirá las entradas de WordPress (la información de nuestros productos) como prop. Este componente también llamará al componente SnipcartButton que creará después. Para ello, añade lo siguiente al archivo:

import SnipcartButton from "./snipcart-button";

const ProductCard = ({post}) => {
    const product = {
        name: post.acf.product_name,
        id: post.id,
        price: post.acf?.price,
        image: post.acf?.image,
        description: post.acf?.description
    }

    return (
        <article>
            <img src={post.acf.image} />
            <div> {post.acf.description} </div>
            <div>
              <strong> ${post.acf.price} </strong>
            </div>
            <SnipcartButton product={product} />
        </article>
    )
}

export default ProductCard;
 

Ahora vamos a crear nuestro componente SnipcartButton añadiendo lo siguiente en snipcart-button.js:

const SnipcartButton = ({product}) => {
    return (
        <button className="snipcart-add-item"
            data-item-name={product.name}
            data-item-price={product.price}
            data-item-image={product.image}
            data-item-id={product.id}
            data-item-description={product.description}>Add to cart
        </button>
    )
 }

 export default SnipcartButton;



Paso 7: Añadir nuestros componentes a nuestras páginas

Ahora que hemos creado en el último paso a nuestra página de inicio y de producto. Para ello, vamos a sustituir el contenido de packages/twentytwenty-theme/src/components/post.js por el siguiente:

import { styled, connect } from "frontity";
import { useEffect } from "react";
import FeaturedMedia from "./featured-media";
import {
  EntryContent,
  Post as _Post,
  PostHeader,
  PostInner,
  PostTitle,
  PostCaption,
  SectionContainer,
} from "./post-item";
import ProductCard from "./../ecommerce/product-card";

/**
 * The Post component that the TwentyTwenty theme uses for rendering any kind of
 * "post type" (posts, pages, attachments, etc.).
 *
 * It doesn't receive any prop but the Frontity store, which it receives from
 * {@link connect}. The current Frontity state is used to know which post type
 * should be rendered.
 *
 * @param props - The Frontity store (state, actions, and libraries).
 *
 * @example
 * 
 * <Switch>
 *   <Post when={data.isPostType} />
 * </Switch>
 * 
 *
 * @returns The {@link Post} element rendered.
 */
const Post = ({ state, actions, libraries }) => {
  // Get information about the current URL.
  const data = state.source.get(state.router.link);

  // Get the data of the post.
  const post = state.source[data.type][data.id];

  // Get the html2react component.
  const Html2React = libraries.html2react.Component;

  const isProduct = (post) => {
    return !!post.acf.price;
  }

  /**
   * Once the post has loaded in the DOM, prefetch both the
   * home posts and the list component so if the user visits
   * the home page, everything is ready and it loads instantly.
   */
  useEffect(() => {
    actions.source.fetch("/");
  }, [actions.source]);

  // Load the post, but only if the data is ready.
  return data.isReady ? (
    <PostArticle>
      <Header>
        <SectionContainer>
          {/* If the post has categories, render the categories */}
          <PostTitle
            as="h1"
            className="heading-size-1"
            dangerouslySetInnerHTML={{ __html: post.title.rendered }}
          />
          {/* If the post has a caption (like attachments), render it */}
          {post.caption && (
            <PostCaption
              dangerouslySetInnerHTML={{ __html: post.caption.rendered }}
            />
          )}
        </SectionContainer>
      </Header>

      {/*
       * If the want to show featured media in the
       * list of featured posts, we render the media.
       */}
      {state.theme.featuredMedia.showOnPost && (
        <FeaturedImage id={post.featured_media} isSinglePost={true} />
      )}

      {/* If the post has a description (like attachments), we render it */}
      {post.description && (
        <PostInner size="thin">
          <EntryContent
            dangerouslySetInnerHTML={{ __html: post.description.rendered }}
          />
        </PostInner>
      )}

      {/* If the post has content, we render it */}
      {post.content && isProduct(post) && (
        <PostInner size="thin">
          <EntryContent>
            <ProductCard post={post} />
          </EntryContent>
        </PostInner>
      )}

      {post.content && !isProduct(post) && (
        <PostInner size="thin">
          <EntryContent>
            <Html2React html={post.content.rendered} />
          </EntryContent>
          {/* If the post has tags, render it */}
          {post.tags && <PostTags tags={tags} />}
        </PostInner>
      )}
    </PostArticle>
  ) : null;
};

export default connect(Post);

const Header = styled(PostHeader)`
  background-color: #fff;
  margin: 0;
  padding: 4rem 0;
  @media (min-width: 700px) {
    padding: 8rem 0;
  }
`;

const PostArticle = styled(_Post)`
  padding-top: 0 !important;
`;

const FeaturedImage = styled(FeaturedMedia)`
  margin-top: 0 !important;
  position: relative;

  > div {
    position: relative;
  }

  &:before {
    background: #fff;
    content: "";
    display: block;
    position: absolute;
    bottom: 50%;
    left: 0;
    right: 0;
    top: 0;
  }
`;
 

Como puedes ver, hemos importado nuestro componente ProductCard y hemos añadido una pequeña función de ayuda para ayudarnos a identificar si el post tiene propiedades de producto. Vamos a utilizar esta función para mostrar la tarjeta de producto o el post normal de WordPress.

También tendremos que cambiar el contenido de packages/twentytwenty-theme/src/components/post-item.js para mostrar nuestra ficha de producto en la página principal.

import { connect, styled } from "frontity";
import Link from "../link";
import FeaturedMedia from "./featured-media";
import ProductCard from "./../ecommerce/product-card";

/**
 * Article Component.
 *
 * It renders the preview of a blog post. Each blog post contains:
 * - Title: clickable title of the post.
 * - FeaturedMedia: the featured image/video of the post.
 *
 * @param props.state - The Frontity state.
 * @param props.libraries - The Frontity libraries.
 * @param props.item - The post entity.
 * @param props.showExcerpt - If the post excerpt should be rendered.
 * @param props.showMedia - If the featured media should be rendered.
 *
 * @returns React element.
 */
const PostItem = ({
  state,
  libraries,
  item,
  showMedia = true,
}) => {

  const post = state.source[item.type][item.id];
  const { Component: Html2React } = libraries.html2react;
  return (
    <Post>
      <PostHeader>
        <SectionContainer>
          {/* The clickable heading for the post */}
          <PostLink link={item.link}>
            <PostItemTitle
              className="heading-size-1"
              dangerouslySetInnerHTML={{ __html: item.title.rendered }}
            />
          </PostLink>
        </SectionContainer>
      </PostHeader>

      {/*
       * If the want to show featured media in the
       * list of featured posts, we render the media.
       */}
      {state.theme.featuredMedia.showOnArchive && showMedia && (
        <FeaturedMedia id={item.featured_media} />
      )}

      {post && post.content && (
        <PostInner size="thin">
          <EntryContent>
            <ProductCard post={post} />
          </EntryContent>
        </PostInner>
      )}
    </Post>
  );
};

// Connect the Item to gain access to `state` as a prop
export default connect(PostItem);

// All styles :)

export const Post = styled.article`
  &:first-of-type {
    padding: 4rem 0 0;
  }

  @media (min-width: 700px) {
    &:first-of-type {
      padding: 8rem 0 0;
    }
  }
`;

export const PostHeader = styled.header`
  text-align: center;
`;

// Header sizes bases on style.css
const maxWidths = {
  thin: "58rem",
  small: "80rem",
  medium: "100rem",
};

/**
 * Return a CSS size depending on the value of the `size` prop received (see
 * {@link maxWidths}).
 *
 * @param props - Component props, including a `size` one.
 * @returns Size in CSS units.
 */
const getMaxWidth = (props) => maxWidths[props.size] || maxWidths["medium"];

export const SectionContainer = styled.div`
  margin-left: auto;
  margin-right: auto;
  width: calc(100% - 4rem);
  max-width: ${getMaxWidth};

  @media (min-width: 700px) {
    width: calc(100% - 8rem);
  }
`;

export const PostItemTitle = styled.h2`
  margin: 0;
  @media (min-width: 700px) {
    font-size: 6.4rem;
  }
`;

export const PostTitle = styled.h1`
  margin: 0;
`;

export const PostCaption = styled(SectionContainer)`
  /* .section-inner.max-percentage */
  margin-left: auto;
  margin-right: auto;
  max-width: ${getMaxWidth({ size: "small" })};
  width: 100%;

  /* .singular .intro-text */
  margin-top: 2rem;
  font-size: 2rem;
  letter-spacing: -0.0315em;
  line-height: 1.4;

  @media (min-width: 700px) {
    margin-top: 2.5rem;
    font-size: 2.6rem;
  }
  @media (min-width: 1000px) {
    font-size: 2.8rem;
  }
  @media (min-width: 1220px) {
    font-size: 3.2rem;
    letter-spacing: -0.03125em;
    line-height: 1.375;
  }
`;

const PostLink = styled(Link)`
  color: #000000;
  text-decoration: none;
  display: inline-block;
  &:hover {
    text-decoration: underline;
  }
`;

export const PostInner = styled(SectionContainer)`
  padding-top: 5rem;
  @media (min-width: 700px) {
    padding-top: 8rem;
  }
`;

export const EntryContent = styled.div`
  line-height: 1.5;
  max-width: 58rem;
  font-family: "Hoefler Text", Garamond, "Times New Roman", serif;
  letter-spacing: normal;

  @media (min-width: 700px) {
    font-size: 2.1rem;
  }

  > *:first-of-type {
    margin-top: 0;
  }

  figure {
    margin: 2em 0;
    max-width: 100%;
  }

  h1,
  h2,
  h3,
  h4,
  h5,
  h6,
  cite,
  figcaption,
  table,
  address,
  .wp-caption-text,
  .wp-block-file {
    font-family: "Inter", -apple-system, BlinkMacSystemFont, "Helvetica Neue",
      Helvetica, sans-serif;
  }

  h1,
  h2,
  h3,
  h4,
  h5,
  h6 {
    margin: 3.5rem auto 2rem;
  }

  @media (min-width: 700px) {
    h1,
    h2,
    h3 {
      margin: 6rem auto 3rem;
    }

    h4,
    h5,
    h6 {
      margin: 4.5rem auto 2.5rem;
    }
  }
`;

Y ahora deberías poder ver tus productos, y el botón ‘Añadir al carrito’ directamente desde la página de inicio.



Paso 8: Añadir un botón para ver el carrito

Ahora vamos a añadir un botón en la cabecera para ver el carrito.

Con el tema Frontity instalado, tenemos dos vistas; móvil y escritorio. Aprovecharemos el estilo del componente de búsqueda predefinido para crear nuestro botón.

Primero, vamos a crear un archivo cart-button.js en packages/twenty-theme/src/components/ con el siguiente contenido en él:

import { connect, styled } from "frontity";
import {
  BaseToggle,
  ToggleWrapper,
} from "./navigation/nav-toggle";

const CartButton = ({ state, actions }) => {

  return (
    <HeaderToggle>
      <ToggleWrapper>
                <BaseToggle className="snipcart-checkout">
           
        </BaseToggle>
      </ToggleWrapper>
    </HeaderToggle>
  );
};

export default connect(CartButton);

const HeaderToggle = styled.div`
  display: none;

  @media (min-width: 1000px) {
    display: flex;
    flex-shrink: 0;
    margin-right: -3rem;
    margin-left: 3rem;
  }

  @media (min-width: 1220px) {
    margin-right: -4rem;
    margin-left: 4rem;
  }
`;

A continuación, añadiremos nuestro botón de carrito móvil en packages/twentytwenty-theme/src/components/mobile creando un cart-button.js que contenga este código:

import { connect, styled } from "frontity";
import {
  CartToggle,
  ToggleWrapper,
} from "../navigation/nav-toggle";

const MobileCartButton = ({ state, actions }) => {

  return (
    <ToggleWrapper>
      <ShowMobile>
                <BaseToggle className="snipcart-checkout">
           
        </BaseToggle>
      </ShowMobile>
    </ToggleWrapper>
  );
};

export const ShowMobile = styled.div`
  display: inline-block;

  @media (min-width: 1000px) {
    display: none;
  }
`;
export default connect(MobileCartButton);

Con estos componentes creados, tendremos que definirlos en los componentes de la cabecera:


packages/src/components/header.js

import { connect, Global, Head, styled } from "frontity";
//..
import CartButton from "./cart-button";
import MobileCartButton from "./mobile/cart-button";

return (
    <PageHeader bg={headerBg} id="site-header">
      <HeaderInner>
        <TitleWrapper>
          {/* Cart button on mobile */}
          <MobileCartButton />

          {/* Heading and Description of the site */}
          <TitleGroup>
            <SiteTitle>
              <StyledLink link="/">{title}</StyledLink>
            </SiteTitle>
            <SiteDescription>{description}</SiteDescription>
          </TitleGroup>

          {/* Mobile menu button and modal */}
          <MobileMenuButton />
          <MobileMenuModal />
        </TitleWrapper>

        <HeaderNavigationWrapper>
          {/* Desktop navigation links */}
          <Navigation />
          {/* Desktop cart button */}
          <CartButton />
        </HeaderNavigationWrapper>
      </HeaderInner>
    </PageHeader>
  );
};

//..
const HeaderNavigationWrapper = styled.div`
  display: none;

  @media (min-width: 1000px) {
    align-items: center;
    display: flex;
  }
`;

Si actualizas, ahora deberías poder ver el botón de mostrar carrito en la cabecera.



Paso 9: Añadir algo de estilo

El último paso será añadir estilo a nuestro sitio y componentes.

Cuando construimos nuestro proyecto Frontity, instalamos un tema predefinido, pero me gustaría personalizar un poco más la tienda y añadir algo de estilo al componente que hemos creado.

Vamos a añadir algo de estilo al botón “Añadir al carrito” y a nuestros productos.

Para ello, añadiremos una nueva constante llamada snipcartStyled en el archivo de estilos globales ubicado en packages/twentytwenty-theme/src/components/styles/global-styles.js:

const snipcartStyle = (colors) => css`
  .snipcart-add-item {
    padding: 10px;
    border-radius: 4px;
    cursor: pointer;
    transition: .2s ease-out;
    transition-property: color,border-color,background-color,box-shadow;
    cursor: pointer;
    color: white;
    background-color: #1a4db3;
  }
  .snipcart-add-item:hover {
    box-shadow: var(--shadow-buttonPrimary-hover,0 10px 4px -8px rgba(0,0,0,.5));
    background-color: #0d59f2;
  }

  .snipcart-checkout {
    padding: 5px;
    cursor: pointer;
    background: none;
  }

  .product-price {
    display: flex;
    align-items: center;
    font-size: 1.5em;
  }

  .SectionContainer {
    display: flex;
    justify-content: center;
  }
`;

const productStyle = (colors) => css`
  img {
    display: block;
    margin-left: auto;
    margin-right: auto;
    width: 50%;
    max-width: 100px;
    padding: 10px;

  }
  article {
    text-align: center;
    padding: 5px;
  }
  `;

//..

const globalStyle = (colors) =>
  css([
    cssReset,
    documentSetup(colors),
    accessibilitySettings,
    elementBase(colors),
    elementBase700,
    elementBase1220,
    listStyle,
    quoteStyle(colors),
    codeStyle(colors),
    mediaStyle(colors),
    tableStyles(colors),
    snipcartStyle(colors),
  ]);

export default globalStyle;
 

Como puedes ver, también tenemos que añadir este objeto de estilo al array que pasamos a la función css llamada dentro de nuestra función globalStyle.

Eso es todo. Ahora tienes un sitio de comercio electrónico construido en WordPress y React!



Demo en vivo & GitHub repo

Vea la demostración en vivo aquí

Vea el repo de GitHub aquí



Pensamientos finales

Como alguien que no es desarrollador, disfruté y aprecié la facilidad que Frontity aporta a la construcción de un sitio estático con WordPress. También disfruté desarrollando usando sólo JavaScript (una vez que nuestra instancia de WordPress fue construida y desplegada).

La configuración cero de Frontity también es encantadora para trabajar. No tener que configurar el enrutamiento y la API acelera el proceso de desarrollo.

¿Has probado Frontity? Hazme saber en los comentarios cuál es tu opinión sobre este framework y cómo fue tu experiencia.

Categorías : # wordpress

Deja una respuesta

Tu dirección de correo electrónico no será publicada.