One place for hosting & domains

      Page

      How To Build a Bookstore Landing Page with Gatsby and TypeScript


      The author selected the Diversity in Tech Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Landing pages are web pages that promote a product or service, providing a place for customers to land when arriving at a site. For businesses, they often are the destination of links in online advertisments and marketing emails. A commercial landing page’s primary goal is to turn visitors into potential clients or customers. Because of this, building a landing page is a valuable skill for a web developer.

      In this tutorial, you will build a landing page with the following two technologies:

      • Gatsby, a React-based frontend framework designed to generate static websites. Gatsby allows you to generate landing pages quickly, which can be useful when creating many landing pages for different projects.

      • TypeScript is a superset of JavaScript that introduces static types and type-checking at build-time. TypeScript has become one of the most widely used alternatives to JavaScript because of its strong typing system, which alerts developers to problems in code before the code gets into proudction. For the landing page, TypeScript will help guard against invalidly typed data for dynamic values, such as the sales pitch text or the signup form input.

      The example landing page you will build in this tutorial will promote a bookstore, and will include the following common components of a landing page:

      • A header for the bookstore
      • A hero image that relates to the store
      • A sales pitch with a list of features/services
      • A email signup form that can be used to add the reader to a mailing list about the business

      The sample project will look like the following image by the end of the tutorial:

      Resulting landing page from following this tutorial, displaying the header, hero image, and sales pitch

      Prerequisites

      Step 1 — Refactoring the Header and Layout Components

      In this step, you will begin by refactoring the existing header.tsx and layout.tsx components of the bookstore-landing-page project that you created in the prerequisite tutorial. This will include replacing default type-definitions with custom-type interfaces and revising some GraphQL queries. Once you have completed this step, you will have populated the header of your landing page with the page’s title and description and created a layout component to implement future components.

      The Header component will display the title and description of your page at the top of the browser window. But before refactoring this component, you will open the gatsby-config.js file in the project’s root directory to update the site’s metadata. Later, you will query gatsby-config.js from the Layout component to retrieve this data.

      Open gatsby-config.js in your text editor of choice. Under siteMetaData in the exported module, change the value of title and description to the name of the bookstore and a business slogan, as shown in the following highlighted code:

      bookstore-landing-page/gatsby-config.js

      module.exports = {
        siteMetadata: {
          title: `The Page Turner`,
          description: `Explore the world through the written word!`,
          author: `@gatsbyjs`,
        },
        plugins: [
          ...
      

      After making these changes, save and close the gatsby-config.js file.

      Next, inside the bookstore-landing-page/src/components directory, open the header.tsx file. From here you will refactor the <Header /> component to use TypeScript typing instead of the default PropTypes. Make the following changes to your code:

      bookstore-landing-page/src/components/header.tsx

      import * as React from "react"
      import { Link } from "gatsby"
      
      interface HeaderProps {
        siteTitle: string,
        description: string
      }
      
      const Header = ({ siteTitle, description }: HeaderProps) => (
        ...
      )
      
      export default Header
      

      You deleted the Header.PropTypes and Header.defaultProps objects after the Header declaration and replaced them with a custom-type interface HeaderProps, using the siteTitle and description properties. Then, you added description to the list of arguments passed to the functional component and assigned them to the HeaderProps type. The newly defined HeaderProps interface will act as a custom type for the arguments passed to the <Header/> component from the GraphQL query in the <Layout/> component.

      Next, in the JSX of the <Header /> component, change the styling in the opening header tag so the background color is blue and the text is center-aligned. Keep siteTitle in the embedded <Link/> component, but add description to a separate <h3/> tag and give it a font color of white:

      bookstore-landing-page/src/components/header.tsx

      ...
      
      const Header = ({ siteTitle, description }: HeaderProps) => (
        <header
          style={{
            background: `#0069ff`,
            textAlign: `center`,
          }}
        >
          <div
            style={{
              margin: `0 auto`,
              maxWidth: 960,
              padding: `1.45rem 1.0875rem`,
            }}
          >
            <h1 style={{ margin: 0 }}>
              <Link
                to="/"
                style={{
                  color: `white`,
                  textDecoration: `none`,
                }}
              >
                {siteTitle}
              </Link>
            </h1>
            <h3 style={{
              color: 'white'
            }}>
              {description}
            </h3>
          </div>
        </header>
      )
      
      export default Header
      

      Now you will have inline styling when data is passed to this component.

      Save the changes in the header.tsx file, then run gatsby develop and go to localhost:8000 on your browser. The page will look like the following:

      Landing page with title rendered in header

      Notice the description has not yet been rendered. In the next step, you will add this to the GraphQL query in layout.tsx to ensure that it is displayed.

      With the <Header/> component ready, you can now refactor the default <Layout/> component for the landing page.

      Layout

      The <Layout /> component will wrap your landing page, and can help share styles and formatting for future pages on your site.

      To start editing this component, open layout.tsx in your text editor. Delete the default type definitions at the end of the file and define a new interface named LayoutProps after the import statements. Then, assign the interface type to the arguments passed to <Layout/>:

      bookstore-landing-page/src/components/layout.tsx

      /**
       * Layout component that queries for data
       * with Gatsby's useStaticQuery component
       *
       * See: https://www.gatsbyjs.com/docs/use-static-query/
       */
      
      import * as React from "react"
      import { useStaticQuery, graphql } from "gatsby"
      
      import Header from "./header"
      import "./layout.css"
      
      interface LayoutProps {
        children: ReactNode
      }
      
      const Layout = ({ children }: LayoutProps) => {
        ...
      }
      
      default export Layout
      

      The interface uses the ReactNode type, which you imported with the React library. This type definition applies to most React child components, which is what <Layout/> renders by default. This will enable you to define a custom-type interface for <Layout/>.

      Next, revise the default GraphQL query located inside the <Layout/> component. Inside of the siteMetaData object, add the description that was set in gatsby-config.js. Then, like with siteTitle, store the fetched value in a new description variable:

      bookstore-landing-page/src/components/layout.tsx

      ...
      
      const Layout = ({ children }: LayoutProps) => {
        const data = useStaticQuery(graphql`
          query SiteTitleQuery {
            site {
              siteMetadata {
                title
                description
              }
            }
          }
        `)
      
        const siteTitle = data.site.siteMetadata?.title || `Title`
        const description = data.site.siteMetadata?.description || 'Description'
      
       ...
      
      

      Now you can pass description as a prop to the <Header/> component in the layout’s returned JSX. This is important because description was defined as a required property in the HeaderProps interface:

      bookstore-landing-page/src/components/layout.tsx

      
      ...
      
        return (
          <>
            <Header siteTitle={siteTitle} description={description}/>
            ...
          </>
        )
      
      export default Layout
      
      

      Save and exit from the layout.tsx file.

      As a final change to your layout, go into layouts.css to make a styling change by centering all text in the body of the page:

      bookstore-landing-page/src/components/layout.css

      ...
      
      /* Custom Styles */
      
      body {
        margin: 0;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        color: hsla(0, 0%, 0%, 0.8);
        font-family: georgia, serif;
        font-weight: normal;
        word-wrap: break-word;
        font-kerning: normal;
        -moz-font-feature-settings: "kern", "liga", "clig", "calt";
        -ms-font-feature-settings: "kern", "liga", "clig", "calt";
        -webkit-font-feature-settings: "kern", "liga", "clig", "calt";
        font-feature-settings: "kern", "liga", "clig", "calt";
        text-align: center;
      }
      
      ...
      

      Save and close the layout.css file, then start the development server and render your site in the browser. You will now find the description value rendered in the header:

      Landing page with rendered header but no content

      Now that you have refactored the base files for your Gatsby site, you can add a hero image to your page to make it more visually appealing to customers.

      Step 2 — Adding a Hero Image

      A hero image is a visual that lends support to the product or service in the landing page. In this step, you will download an image for your bookstore landing page and render it on the site using the <StaticImage /> component of the gatsby-plugin-image plugin.

      Note: This project is using Gatsby version 3.9.0, so you won’t be able to use the deprecated gatsby-image package. This package was replaced with gatsby-plugin-image. This new plugin, along with help from gatsby-plugin-sharp, will render responsive images with processing functionality.

      First, download the bookshelf image from Unsplash, a site that provides images that you can use freely:

      • curl https://images.unsplash.com/photo-1507842217343-583bb7270b66 -o src/images/bookshelf.png

      This command uses curl to download the image. The -o flag designates the output, which you have set to be a file named bookshelf.png in the images directory.

      Now open the src/pages/index.tsx file. The Gatsby default starter template already has a <StaticImage/> component, so replace the attributes to point to your newly downloaded image:

      bookstore-landing-page/src/pages/index.tsx

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import Seo from "../components/seo"
      
      const IndexPage = () => (
        <Layout>
          <Seo title="Home" />
          <StaticImage
            src="https://www.digitalocean.com/community/tutorials/images/bookshelf.png"
            alt="Bookshelf hero image"
          />
        </Layout>
      )
      
      export default IndexPage
      

      You added a src attribute to direct Gatsby to the correct image in your images directory, then added the alt attribute to provide alternative text for the image.

      Save and close the file, then restart the development server. Your page will now have the downloaded bookshelf image rendered at its center:

      Rendered landing page with bookshelf image.

      With your image now rendered on your site, you can move on to adding some content to the page.

      Step 3 — Creating a Sales Pitch and Features Component

      For the next part of the landing page, you are going to build a new component that holds the sales pitch for your bookstore. This will explain why your customers should come to your store.

      Inside of bookstore-landing-page/src/components, go ahead and create a new file titled salesPitchAndFeatures.tsx. Inside the new file, import React, create a new functional component called SalesPitchAndFeatures, and export it:

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      import * as React from "react"
      
      const SalesPitchAndFeatures = () => {
        <>
        </>
      }
      
      export default SalesPitchAndFeatures
      

      The interface for this component will include an optional salesPitch property of type string. It will also have a list of features of type Array<string>, which is required:

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      import * as React from "react"
      
      interface SalesPitchAndFeaturesProps {
        salesPitch?: string
        features: Array<string>
      }
      ...
      

      The data for the salesPitch and features will be hard-coded within salesPitchAndFeatures.tsx, but you could also store it in another place (like gatsby-config.js) and query the needed data with GraphQL. The content object will be of type SalesPitchAndFeaturesProps:

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      ...
      
      interface salesPitchAndFeaturesProps {
          salesPitch?: string 
          features: Array<string>
      }
      
      const content: SalesPitchAndFeaturesProps = {
          salesPitch: "Come and expand your world at our bookstore! We are always getting new titles for you to see. Everything you need is here at an unbeatable price!",
          features: [ 
          "Tens of thousands of books to browse through",
          "Engage with the community at a book club meeting",
          "From the classics to the newest publications, there's something for everybody!"
      ]}
      
      const SalesPitchAndFeatures = () => {
          return (
              <>
      
                ...
      

      Notice that the salesPitch prop is a string and the features prop is an array of strings, just as you set them in your interface.

      You’ll also need a function that will display the list of features. Create a showFeatures(f)function.

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      ...
      
      const showFeatures: any = (f: string[]) => {
          return f.map(feature => <li>{feature}</li>)
      }
      
      const SalesPitchAndFeatures = () => {
          return (
              <>
      
                ...
      
      

      The argument f passed into showFeatures is of type Array<string> to be consistent with the array of features of type string. To return the list tranformed into rendered JSX, you use the .map() array method.

      Populate the return statement with your content, wrapped in divs with assigned class names for styling:

      bookstore-landing-page/src/components/salesPitchAndFeatures.tsx

      ...
      
      const SalesPitchAndFeatures = () => {
          return (
              <div className="features-container">
                  <p className="features-info">
                      {content.salesPitch}
                  </p>
                  <ul className="features-list">
                      {showFeatures(content.features)}
                  </ul>
              </div>
          )
      }
      
      export default SalesPitchAndFeatures
      

      Save and close salesPitchAndFeatures.tsx.

      Next, open layout.css to add styling to the class names added in the <SalesPitchAndFeatures/> component:

      bookstore-landing-page/src/components/layout.css

      ...
      .features-container {
        border: 1px solid indigo;
        border-radius: 0.25em;
        padding: 2em;
        margin: 1em auto;
      }
      
      .features-list {
        text-align: left;
        margin: auto;
      }
      

      This adds a border around the sales pitch and features list, then adds spacing between the elements to increase readability.

      Save and close layout.css.

      Lastly, you will render this component on the landing page. Open index.tsx in the src/pages/ directory. Add the <SalesPitchAndFeatures/> component to the rendered layout children:

      bookstore-landing-page/src/pages/index.tsx

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import SalesPitchAndFeatures from "../components/salesPitchAndFeatures"
      import SEO from "../components/seo"
      
      const IndexPage = () => (
        <Layout>
          <SEO title="Home" />
          <div style={{ maxWidth: `450px`, margin: ' 1em auto'}}>
            <StaticImage
              src="https://www.digitalocean.com/community/tutorials/images/bookshelf.png"
              alt="Bookshelf hero image"
            />
            <SalesPitchAndFeatures/>
          </div>
        </Layout>
      )
      
      export default IndexPage
      

      You also added in a div to apply some styling to both the image and the sales pitch.

      Save and exit from the file. Restart your development server and you will find your sales pitch and features list rendered below your image:

      Rendered page with sales pitch and features added

      You now have a sales pitch on your landing page, which will help communicate to potential customers why they should go to your business. Next, you will build the final component for the landing page: an email signup form.

      Step 4 — Creating a Signup Form Component

      An email signup button is a common landing page component that lets the user enter their email address and sign up for more news and information about the product or business. Adding this to your landing page will give the user an actionable step they can take to become your customer.

      To start, create a new file in bookstore-landing-page/src/components called signupForm.tsx. This component won’t have any custom types but will have an event handler, which has its own special React-based type.

      First, build the <SignUpForm/> component and its return statement, with a header inside:

      bookstore-landing-page/src/components/signupForm.tsx

       import * as React from "react"
      
      const SignUpForm = () => {
        return (
          <h3>Sign up for our newsletter!</h3>
        )
      }
      
      export default SignupForm
      

      Next, add some markup to create a form element with an onSubmit attribute, initialized to null for now. Soon this will contain the event handler, but for now, finish writing the form with label, input, and button tags:

      bookstore-landing-page/src/components/signupForm.tsx

      import * as React from "react"
      
      const SignUpForm = () => {
        return (
          <React.Fragment>
            <h3>Sign up for our newsletter!</h3>
            <form onSubmit={null}>
              <div style={{display: 'flex'}}>
                  <input type="email" placeholder="email@here"/>
      
              <button type="submit">Submit</button>
              </div>
            </form>
          <React.Fragment>
        )
      }
      
      export default SignupForm
      

      If you type your email address in the text field and click Sign Up on the rendered landing page right now, nothing will happen. This is because you still need to write an event handler that will trigger whenever the form is submitted. A best practice is to write a separate function outside the return statement for the event handler. This function usually has a special e object that represents the triggered event.

      Before writing the separate function, you will write the function in-line to figure out what the static type of the event object is, so that you can use that type later:

      bookstore-landing-page/src/components/signupForm.tsx

      ...
      
          return (
              <React.Fragment>
                  <h3>Sign up for our newsletter!</h3>
                  <form onSubmit={(e) => null}>
                      <div style={{display: 'flex'}}>
                          <input type="email" placeholder="email@here" style={{width: '100%'}}/>
                          <button type="submit">Submit</button>
                      </div>
                  </form>
              </React.Fragment>
          )
      ...
      }
      
      export default SignupForm
      

      If you are using a text editor like Visual Studio Code, hovering your cursor over the e parameter will use TypeScript’s IntelliSense to show you its expected type, which in this case is React.FormEvent<HTMLFormElement>.

      Now that you know what the expected type of your separate function will be, go ahead and use this to write a new, separate function called handleSubmit:

      bookstore-landing-page/src/components/signupForm.tsx

      import * as React from "react"
      
      const SignupForm = () => {
          const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
              e.preventDefault();
              alert(alert('The submit button was clicked! You're signed up!'))
          }
          return (
              <React.Fragment>
                  <h3>Sign up for our newsletter!</h3>
                  <form onSubmit={handleSubmit}>
                      <div style={{display: 'flex'}}>
                        <input type="email" placeholder="email@here"/>
                        <button type="submit">Submit</button>
                      </div>
                  </form>
              </React.Fragment>
        )
      }
      
      export default SignupForm
      

      The handleSubmit function will now trigger a browser alert when the form is submitted. Note that this is a temporary placeholder; to actually add the user to an email list, you would have to connect this to a back-end database, which is beyond the scope of this tutorial.

      Save and close the signupForm.tsx file.

      Now, open the index.tsx file and add the new <SignupForm/> component:

      bookstore-landing-page/src/pages/index.tsx

      import * as React from "react"
      import { StaticImage } from "gatsby-plugin-image"
      
      import Layout from "../components/layout"
      import SalesPitchAndFeatures from "../components/salesPitchAndFeatures"
      import SignupForm from "../components/signupForm"
      import Seo from "../components/seo"
      
      const IndexPage = () => (
        <Layout>
          <Seo title="Home" />
          <div style={{ maxWidth: `450px`, margin: ' 1em auto'}}>
            <HeroImage />
            <SalesPitchAndFeatures />
            <SignupForm />
          </div>
        </Layout>
      )
      
      export default IndexPage
      

      Save and exit the file.

      Restart your development server, and you will find your completed landing page rendered in your browser, along with the email signup button:

      Email signup button rendered below the feature list on the landing page.

      You have now finished building all the core components of your bookstore landing page.

      Conclusion

      Because Gatsby creates fast static websites and TypeScript allows for data to be statically typed, building a landing page makes for an excellent use case. You can shape the types of its common elements (header, hero image, email signup, etc.) so that incorrect forms of data will trigger errors before they go into production. Gatsby provides the bulk of the structure and styling of the page, allowing you to build on top of it. You can use this knowledge to build other landing pages to promote other products and services quicker and more efficiently.

      If you’d like to learn more about TypeScript, check out our How To Code in TypeScript series, or try our How To Create Static Web Sites with Gatsby.js series to learn more about Gatsby.



      Source link

      How To Build a Responsive About Me Page with Laravel, Sail, and Tailwind CSS


      Introduction

      Laravel Sail is a Docker development environment included by default in Laravel since version 8. It allows you to quickly get a PHP development environment up and running, tailored for running Laravel applications with built-in support for NPM / Node.

      In this guide, you’ll bootstrap a new Laravel application with Laravel Sail and create a styled “about me” landing page using Tailwind CSS, a utility-first CSS framework designed for rapidly building custom user interfaces. At the end, you’ll have a base that you can use to further develop a Laravel application using Tailwind CSS for the front end and Sail for the development environment.

      Laravel Tailwind Responsive About Me Page

      If you’d prefer to follow along from an existing codebase rather than creating the project from scratch, you can access the finished demo application code at do-community/laravel-tailwind-starter on GitHub.

      Prerequisites

      Although the code shared in this guide should work seamlessly across multiple environments and systems, the instructions explained here were tested within an Ubuntu 20.04 local system running Docker and Docker Compose. Regardless of your base operating system, here’s what you’ll need to set up in order to get started:

      • Docker installed on your local machine. If you’re running Ubuntu 20.04, you can follow Steps 1 and 2 of How To Install and Use Docker on Ubuntu 20.04 to set it up. Windows and MacOS users need to install Docker Desktop instead.
      • Docker Compose installed on your local machine. Docker Compose comes included by default with Docker Desktop for both Windows and MacOS systems, but Linux users need to install the Compose executable, following Step 1 of How To Install and Use Docker Compose on Ubuntu 20.04.
      • A code editor for PHP (optional). A code editor helps making code easier to read and to format, and can improve your productivity by pointing out issues before you execute your code. You can follow our guide on How To Set Up Visual Studio Code for PHP Projects to set up VSCode, a free code editor, within your local development environment.

      Step 1 — Creating a New Laravel Application Using the Laravel Builder Script

      To get started, you’ll download and execute the official Laravel builder script, which will pull in the necessary Docker container images to build your development environment and then bootstrap a new application in your current folder. This installation method doesn’t require you to have PHP installed on your system, requiring only that you download and execute the builder script that will set up the Docker environment where you can run the actual Laravel installer.

      At the end, the script asks for your sudo password to make sure the application folder has correct permissions for your system user. You can access the script URL from your browser to verify its contents before running the next command. In this example, we’re using the name myapp, but you are free to replace this with a different name:

      curl -s https://laravel.build/myapp | bash
      

      Output

      Unable to find image 'laravelsail/php80-composer:latest' locally latest: Pulling from laravelsail/php80-composer 852e50cd189d: Pull complete 0266fc315b01: Pull complete … Application ready! Build something amazing. Sail scaffolding installed successfully. Please provide your password so we can make some final adjustments to your application's permissions. [sudo] password for sammy: Thank you! We hope you build something incredible. Dive in with: cd myapp && ./vendor/bin/sail up

      When the installation is finished, access the new application directory and get the Sail environment up with:

      • cd myapp
      • ./vendor/bin/sail up

      This will bring the environment up in foreground mode, so you can follow up with the logs from all running containers. You’ll see a few different services getting started, using different ports to communicate between each other:

      Output

      ... mailhog_1 | [HTTP] Binding to address: 0.0.0.0:8025 ... laravel.test_1 | Starting Laravel development server: http://0.0.0.0:80 ... meilisearch_1 | Server listening on: "http://0.0.0.0:7700" ... mysql_1 | 2021-06-23T01:15:24.327234Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.25' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL. ... selenium_1 | 01:14:57.417 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 4444 ... redis_1 | 1:M 23 Jun 2021 01:14:54.243 * Running mode=standalone, port=6379. …

      These are services configured by default within Sail environments. For more information about each of them, refer to the official Sail documentation.

      Next, access the application from your browser at:

      http://localhost
      

      If all steps were successful, you’ll see a page like this:
      Laravel welcome page

      The application is now bootstrapped.

      You can now stop the Sail environment that is running on your terminal by typing CTRL+C.

      Step 2 — Using Laravel Sail

      Laravel Sail offers several shortcuts to manage your development environment. Most commands and arguments are based on the default Docker Compose API.

      Controlling the Environment

      To bring the environment up in background mode, you can run:

      To stop a Sail environment that was previously initiated in background mode, run:

      This won’t delete attached networks or volumes.

      To bring back an environment that was previously stopped with a sail stop command, you can use:

      To stop an environment and also delete all associated resources such as volumes and networks, you can use the sail down command. Please notice that this command will delete any data that was previously created and is only available inside containers, such as records stored in a database.

      Output

      Stopping laravel-tailwind-starter_laravel.test_1 ... done Stopping laravel-tailwind-starter_redis_1 ... done Stopping laravel-tailwind-starter_selenium_1 ... done Stopping laravel-tailwind-starter_mysql_1 ... done Stopping laravel-tailwind-starter_mailhog_1 ... done Stopping laravel-tailwind-starter_meilisearch_1 ... done Removing laravel-tailwind-starter_laravel.test_1 ... done Removing laravel-tailwind-starter_redis_1 ... done Removing laravel-tailwind-starter_selenium_1 ... done Removing laravel-tailwind-starter_mysql_1 ... done Removing laravel-tailwind-starter_mailhog_1 ... done Removing laravel-tailwind-starter_meilisearch_1 ... done Removing network laravel-tailwind-starter_sail

      Checking Status and Logs

      If your environment is down, bring it back up with:

      When your environment is up and running, you can check the status of all active containers with:

      Output

      Name Command State Ports --------------------------------------------------------------------------------------------------------------------------------- myapp_laravel.test_1 start-container Up 0.0.0.0:80->80/tcp,:::80->80/tcp, 8000/tcp myapp_mailhog_1 MailHog Up 0.0.0.0:1025->1025/tcp,:::1025->1025/tcp, 0.0.0.0:8025->8025/tcp,:::8025->8025/tcp myapp_meilisearch_1 tini -- /bin/sh -c ./meili ... Up (healthy) 0.0.0.0:7700->7700/tcp,:::7700->7700/tcp myapp_mysql_1 docker-entrypoint.sh mysqld Up (healthy) 0.0.0.0:3306->3306/tcp,:::3306->3306/tcp, 33060/tcp myapp_redis_1 docker-entrypoint.sh redis ... Up (healthy) 0.0.0.0:6379->6379/tcp,:::6379->6379/tcp myapp_selenium_1 /opt/bin/entry_point.sh Up 4444/tcp

      The output from the sail ps command will tell you which containers related to that specific environment are currently active, which ports are being redirected, and more importantly, in which state each container is. In the previous example output, all services are up.

      To check the containers logs when you’re running your environment in background mode, you can use:

      This will show you the latest logs from all services.

      Attaching to laravel-tailwind-starter_laravel.test_1, laravel-tailwind-starter_mailhog_1, laravel-tailwind-starter_mysql_1, laravel-tailwind-starter_redis_1, laravel-tailwind-starter_selenium_1, laravel-tailwind-starter_meilisearch_1
      ...
      mysql_1         | 2021-06-24T15:08:06.435530Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.25'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server - GPL.
      ...
      meilisearch_1   | [2021-06-24T15:16:38Z INFO  actix_web::middleware::logger] 127.0.0.1:60874 "GET /health HTTP/1.1" 200 22 "-" "Wget" 0.000056
      ...
      laravel.test_1  | [Thu Jun 24 15:08:07 2021] PHP 8.0.7 Development Server (http://0.0.0.0:80) started
      ...
      
      selenium_1      | 15:08:06.864 INFO [SeleniumServer.boot] - Selenium Server is up and running on port 4444
      ...
      redis_1         | 1:M 24 Jun 2021 15:08:05.280 * Ready to accept connections
      ...
      mailhog_1       | 2021/06/24 15:08:05 Serving under http://0.0.0.0:8025/
      

      You can also see logs per service by providing an additional argument to the command call:

      • ./vendor/bin/sail logs redis

      Output

      Attaching to laravel-tailwind-starter_redis_1 redis_1 | 1:C 24 Jun 2021 15:08:05.278 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo redis_1 | 1:C 24 Jun 2021 15:08:05.278 # Redis version=6.2.4, bits=64, commit=00000000, modified=0, pid=1, just started ... redis_1 | 1:M 24 Jun 2021 15:08:05.280 * RDB memory usage when created 0.77 Mb redis_1 | 1:M 24 Jun 2021 15:08:05.280 * DB loaded from disk: 0.000 seconds redis_1 | 1:M 24 Jun 2021 15:08:05.280 * Ready to accept connections

      Running Artisan and Composer

      While working on your Laravel application, you’ll often be required to run artisan commands to build, test, and manage your application. You’ll also need to run composer commands to manage your PHP dependencies. In addition to the default Docker Compose API, Sail offers helpful shortcuts to execute these commands in your application container (myapp_laravel.test_1 in the example output). With a regular Docker Compose setup, running Artisan would look like this:

      With Docker Compose Only

      docker-compose exec app php artisan
      

      With Sail, the equivalent call is shortened to:

      Running Artisan With Sail

      • ./vendor/bin/sail artisan

      You can run Composer in a similar way:

      Running Composer With Sail

      • ./vendor/bin/sail composer

      For more information about all features and commands available, please visit the official Laravel Sail Documentation.

      You’re now familiar with how to manage your Sail development environment and how to run commands on the application container. In the next step, you’ll set up Tailwind CSS to design and style your landing page.

      Step 3 — Setting Up Tailwind CSS with Laravel

      Next, you’ll install and set up Tailwind CSS to build a landing page.

      Make sure your Sail environment is up and running, then install Laravel’s front end dependencies with the npm command, which is used to download and manage JavaScript packages :

      • ./vendor/bin/sail npm install

      Output

      ... added 1327 packages, and audited 1328 packages in 20s 99 packages are looking for funding run `npm fund` for details ...

      Then, install Tailwind and its dependencies with:

      • ./vendor/bin/sail npm install -D tailwindcss@latest postcss@latest autoprefixer@latest

      Output

      ... added 9 packages, removed 22 packages, changed 7 packages, and audited 1315 packages in 5s 99 packages are looking for funding run `npm fund` for details ...

      Next, you’ll need to create a configuration file for Tailwind. To do that, you’ll use npx, which stands for Node package executer and allows you to execute a Node package. The following npx command will generate a new default Tailwind configuration for your application:

      • ./vendor/bin/sail npx tailwindcss init

      This will create a new configuration file named tailwind.config.js in your project’s root directory with the following content:

      tailwind.config.js

      module.exports = {
        purge: [],
        darkMode: false, // or 'media' or 'class'
        theme: {
          extend: {},
        },
        variants: {
          extend: {},
        },
        plugins: [],
      }
      

      Although there are different ways to set up Tailwind within a project, in this guide we’ll configure Tailwind with Laravel Mix and webpack. Both libraries are used to compile and output front end resources.

      Open the file webpack.mix.js using your code editor. It will look like this:

      mix.js('resources/js/app.js', 'public/js')
          .postCss('resources/css/app.css', 'public/css', [
              //
          ]);
      

      Laravel Mix uses PostCSS to compile CSS resources. Remove the // characters and include the following highlighted line, which will add Tailwind CSS in the list of CSS resources to process:

      Remove the // characters and include the following highlighted line, which requires Tailwind as a PostCSS plugin:

      webpack.mix.js

        mix.js("resources/js/app.js", "public/js")
          .postCss("resources/css/app.css", "public/css", [
           require("tailwindcss"),
          ]);
      

      Save the file after making this change.

      Next, include Tailwind within the application’s main CSS file. Open resources/css/app.css in your code editor and add the following 3 lines to this file:

      resources/css/app.css

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

      Save when finished.

      Next, you’ll need to build the front end assets with NPM:

      • ./vendor/bin/sail npm run dev

      You will receive output that is similar to the following, with a line like the the highlighted Compiled Successfully portion that indicates you have integrated all of the components into your Sail environment:

      Output

      Laravel Mix v6.0.24 ✔ Compiled Successfully in 5515ms ┌───────────────────────────────────────────────────────────────────┬──────────┐ │ File │ Size │ ├───────────────────────────────────────────────────────────────────┼──────────┤ │ /js/app.js │ 597 KiB │ │ css/app.css │ 3.81 MiB │ └───────────────────────────────────────────────────────────────────┴──────────┘ webpack compiled successfully

      Tailwind is now configured and you have built the front end assets for your site.. In the next step, you’ll create a new landing page for your application.

      Step 4 — Creating a Landing Page

      With Tailwind’s configuration in place, you can now start building your application’s front end views. In Laravel, templates are typically stored in the resources/views directory. The page you saw before when opening the application from a browser (http://localhost) is defined in a single template called welcome.blade.php, in that directory.

      In your code editor, open a new file called index.blade.php in the resources/views directory.

      The following example template defines an “about me” HTML page with a few unstyled elements. It uses an example avatar image but you may replace it with an image of your own.

      Create a new img directory in the public application folder:

      Save your preferred image to this new directory, under the name profile_image.png.

      In the following example, notice the use of the highlighted {{ asset… }} helper lines to define paths for both the CSS and the image files. This function outputs the correct public path for application resources located in the public directory.

      Copy this content to your own index.blade.php:

      resources/views/index.blade.php

      <!doctype html>
      <html lang="en">
      <head>
          <meta charset="utf-8">
          <title>Sammy the Shark - About Page</title>
          <meta name="description" content="My Application Description">
          <meta name="author" content="Sammy">
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <link href="https://www.digitalocean.com/community/tutorials/{{ asset('css/app.css') }}" rel="stylesheet">
      </head>
      <body>
      <div>
          <img src="https://www.digitalocean.com/community/tutorials/{{ asset('img/profile_image.png') }}" width="200" alt="avatar"/>
          <h1>Sammy the Shark</h1>
          <p>Content Creator</p>
          <p>Hello, I'm Sammy. I am a friendly shark interested in Linux, coding, and community.
              You can find out more about me in the following links:</p>
          <div>
              <div><a href="https://twitter.com/digitalocean">Twitter</a></div>
              <div><a href="https://www.linkedin.com/company/digitalocean">LinkedIn</a></div>
              <div><a href="https://instagram.com/thedigitalocean">Instagram</a></div>
          </div>
      </div>
      </body>
      </html>
      

      Save the file when you’re finished editing its contents.

      Now edit the routes/web.php file to modify the main route so that it uses the newly created template. Open that file using your code editor, and replace welcome with index to change the view used by the main application endpoint. This is how the updated route declaration will look like once you’re finished:

      routes/web.php

      Route::get('/', function () {
          return view("https://www.digitalocean.com/community/tutorials/index');
      });
      

      Save the file. You can now reload the application page in your browser to see the new index page. You’ll see a page like this:
      Laravel about me page with tailwind - base template

      By default, Tailwind removes all styling from elements, which gives you freedom to build up your views by combining and mixing Tailwind CSS utility classes. In the next section, you’ll learn how to combine some of these utility classes in order to create a responsive “about me” page.

      Step 5 — Styling Your Landing Page with Tailwind CSS

      Building responsive pages is an important front end development requirement, since users may access your website or application from many different devices, each with different screen sizes.

      Tailwind offers selectors that are able to apply styles per screen size. This way, you can create responsive containers by setting up the smallest width as default, and appending the additional responsive sizes for bigger screens. For instance, an element set with class="w-3/4 lg:w-1/2" will set a default width of 3 quarters the width of the parent element, which will be valid for smaller screens, but for larger screens (lg: selector), it will use half the width of the parent element.

      Notice that you can combine the responsive selectors with any utility class, not only those associated with an element’s size. You can, for instance, hide an element at a certain breakpoint, change its colors, or even turn it into a grid with a variable number of columns.

      You can find all available responsive selectors and their equivalent breaking points at the official Tailwind documentation.

      The following template sets up a responsive content area at the center of the page, using background gradients and an example avatar image. For the buttons, it uses a grid flow system that will break the container into three columns starting at medium screens, but will make the buttons occupy the full container size when the page is accessed from smaller screens.

      Open the resources/views/index.blade.php file that you created in the previous step in your code editor and replace the contents with the following template:

      resources/views/index.blade.php

      <!doctype html>
      <html lang="en">
      <head>
          <meta charset="utf-8">
          <title>Sammy the Shark - About Page</title>
          <meta name="description" content="My Application Description">
          <meta name="author" content="Sammy">
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <link href="https://www.digitalocean.com/community/tutorials/{{ asset("css/app.css') }}" rel="stylesheet">
      </head>
      <body class="bg-gradient-to-r from-blue-400 via-purple-600 to-blue-700">
      
      <div class="w-3/4 lg:w-1/2 mx-auto rounded-md bg-gray-200 shadow-lg m-20 p-10 text-center">
          <img src="https://www.digitalocean.com/community/tutorials/{{ asset("img/profile_image.png') }}" class="w-32 lg:w-1/6 bg-blue-600 mx-auto rounded-lg mb-4" alt="avatar"/>
          <h1 class="text-3xl">Sammy the Shark</h1>
          <p class="text-gray-500 pb-4">Content Creator</p>
          <p class="text-gray-700 mb-6">Hello, I'm Sammy. I am a friendly shark interested in Linux, coding, and community.
              You can find out more about me in the following links:</p>
      
          <div class="grid grid-cols-1 md:grid-cols-3 grid-flow-row gap-6">
              <div class="px-4 py-2 bg-blue-600 text-gray-100 rounded-md mr-4 hover:bg-blue-700"><a href="https://twitter.com/digitalocean">Twitter</a></div>
              <div class="px-4 py-2 bg-blue-600 text-gray-100 rounded-md mr-4 hover:bg-blue-700"><a href="https://www.linkedin.com/company/digitalocean">LinkedIn</a></div>
              <div class="px-4 py-2 bg-blue-600 text-gray-100 rounded-md mr-4 hover:bg-blue-700"><a href="https://instagram.com/thedigitalocean">Instagram</a></div>
          </div>
      
      </div>
      </body>
      </html>
      

      This template will produce the following page:

      Final result - Laravel about me page with Tailwind

      And this is how the page adapts to different screen sizes:

      Laravel Tailwind Responsive About Me Page

      In the updated template, each HTML element has a number of Tailwind classes and selectors applied to them. Tailwind uses specific nomenclature for its utility classes to create responsive layouts. Each of the following is used in the example template to create the final result:

      • bg-gradient-to-r: creates a gradient from left to right, using the specified colors.
      • w-1/3: sets the width of the element to one third (1/3) of the parent’s element width. There are many different ways to set up an element’s width within Tailwind.
      • mx-auto: centers the element.
      • rounded-md: creates rounded corners, “medium” (md) in size.
      • shadow-lg: creates a shadow effect, “large” (lg) in size.
      • mr-* and other m variants: used to set up an element’s margins.
      • pb-* and other p variants: used to set up an element’s padding.
      • hover:bg-blue-700: changes the background color of the selected element on mouse hover.

      Check Tailwind’s official documentation for a complete reference of all available utility classes.

      Conclusion

      In this tutorial, you’ve bootstrapped a new Laravel application using Laravel Sail and Tailwind CSS. You also created a responsive “about me” landing page using Tailwind’s powerful utility classes.

      If you’d like to build a more complex landing page and learn more about Tailwind in the process, you can follow our guide on How To Build a Styled Landing Page with Tailwind CSS for detailed instructions on how to create a complete website page with this framework.

      If you’d like to learn more about Laravel in a project-based guide, you can refer to our How To Build a Links Landing Page in PHP with Laravel series. For more PHP content, check our PHP tag.





      Source link