One place for hosting & domains

      State

      How To Debug Components, State, and Events with Vue.js Devtools


      The author selected Open Sourcing Mental Illness to receive a donation as part of the Write for DOnations program.

      Introduction

      When debugging a Vue application, it is common to iterate on your code, making changes then restarting the server to see if they fixed the problem in your browser-rendered site. But moving between your source code and your browser can be a time-consuming process. To make debugging more efficient, you can use a browser extension like Vue.js Devtools. Vue.js Devtools is a browser extension for Chrome and Firefox and a stand-alone Electron app created by Guillaume Chau and Evan You of the Vue.js Core Team. It provides the developer with a visual representation of the inner workings of their Vue application so that you can inspect components, data, computed properties, and events from the browser, as well as Vuex actions, getters, and mutations.

      With Devtools, you will be able to see exactly when an event is executed and what payload it has. In addition to events, you will also be able to see all of your components displayed in a DOM-like format where you can analyze component properties, such as data, computed properties, and props.

      In this tutorial, you will set up a sample Vue application, install Vue.js DevTools in your browser, then add new features to your app while testing them with the browser extension. This will serve as a first step to debugging your code with Vue.js Devtools.

      Prerequisites

      Step 1 — Setting Up the Example Application

      In this step, you will put together a sample application that you can use in later steps to try out the Devtools browser extension. To get started, generate a new Vue application via the Vue CLI. To do this, open your terminal window and run the following command.

      • vue create favorite-airports

      When prompted with the setup options for your Vue app, select Manually select features. Then select Choose Vue version, Babel, and Vuex. Once selected, hit the RETURN key and continue filling out the prompts as follows:

      Output

      Vue CLI v4.5.15 ? Please pick a preset: Manually select features ? Check the features needed for your project: Choose Vue version, Babel, Vuex ? Choose a version of Vue.js that you want to start the project with 3.x ? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files ? Save this as a preset for future projects? No

      Once the favorite-airports project is created, open your terminal window and cd into the favorite-airports root folder. Once you’ve changed into the directory, create a new directory with the mkdir command:

      In your text editor of choice, create and open an data/airports.js file and add in the following:

      favorite-airports/src/data/airports.js

      export default [
        {
          name: 'Cincinnati/Northern Kentucky International Airport',
          abbreviation: 'CVG',
          city: 'Hebron',
          state: 'KY',
        },
        {
          name: 'Seattle-Tacoma International Airport',
          abbreviation: 'SEA',
          city: 'Seattle',
          state: 'WA',
        },
        {
          name: 'Minneapolis-Saint Paul International Airport',
          abbreviation: 'MSP',
          city: 'Bloomington',
          state: 'MN',
        },
        {
          name: 'Louis Armstrong New Orleans International Airport',
          abbreviation: 'MSY',
          city: 'New Orleans',
          state: 'LA',
        },
        {
          name: `Chicago O'hare International Airport`,
          abbreviation: 'ORD',
          city: 'Chicago',
          state: 'IL',
        },
        {
          name: `Miami International Airport`,
          abbreviation: 'MIA',
          city: 'Miami',
          state: 'FL',
        }
      ]
      

      This is an array of objects consisting of a few airports in the United States. In this application, you are going to iterate through this data to generate cards consisting of the name, abbreviation, city, and state properties. You will use this data to explore events and dispatches in Vue.js Devtools.

      Save the airports.js file, then create a single-file component (SFC) with the name AirportCard.vue. This file will live in the components directory of your project, and will contain all the styles and logic for the airport card.

      Create and open components/AirportCard.vue in your text editor and add the following:

      favorite-airports/src/components/AirportCard.vue

      <template>
        <div class="airport">
          <p>{{ airport.abbreviation }}</p>
          <p>{{ airport.name }}</p>
          <p>{{ airport.city }}, {{ airport.state }}</p>
        </div>
      </template>
      
      <script>
      export default {
        props: {
          airport: {
            type: Object,
            required: true
          }
        }
      }
      </script>
      
      <style scoped>
      .airport {
        border: 3px solid;
        border-radius: .5rem;
        padding: 1rem;
        margin-bottom: 1rem;
      }
      
      .airport p:first-child {
        font-weight: bold;
        font-size: 2.5rem;
        margin: 1rem 0;
      }
      
      .airport p:last-child {
        font-style: italic;
        font-size: .8rem;
      }
      </style>
      

      You may notice that there is some CSS included in this code snippet. In the AirportCard.vue component, the wrapper <div> contains the class of airport. This CSS adds some styling to the generated HTML by adding borders to give each airport the appearance of a “card”. The :first-child and :last-child are pseudo selectors that apply different styling to the first and last p tags in the HTML inside the div with the class of airport. In addition to that, you may also notice that this component contains a prop, which in Vue.js is a way to pass data down from a parent component to a child component.

      Save and exit from the file.

      Before wrapping up the setup, replace the existing App.vue component with the following code:

      favorite-airports/src/App.vue

      <template>
        <div class="wrapper">
          <div v-for="airport in airports" :key="airport.abbreviation">
            <airport-card :airport="airport" />
          </div>
        </div>
      </template>
      
      <script>
      import { ref } from 'vue'
      import allAirports from '@/data/airports.js'
      import AirportCard from '@/components/AirportCard.vue'
      
      export default {
        components: {
          AirportCard
        },
        setup() {
          const airports = ref(allAirports)
          return { airports }
        }
      }
      </script>
      
      <style>
      #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      
      .wrapper {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        grid-column-gap: 1rem;
        max-width: 960px;
        margin: 0 auto;
      }
      
      p,
      h3 {
        grid-column: span 3;
      }
      </style>
      

      This code contains a v-for loop that iterates through the airports.js data and renders a series of AirportCard.vue components with airport data passed in via the prop :airport. Save this code and return to the command line.

      With the project set up, run the local development server using the npm run serve command in your terminal:

      This will start a server on your localhost, usually at port 8080. Open your web browser of choice and visit localhost:8080 to see the following:

      A list of cards rendered in Vue using the v-for directive.

      Your example application is now successfully set up. Throughout this tutorial, you are going to use this application to explore the Devtools browser extension.

      In the next step, you are going to install the Vue.js Devtools on Chromium and Firefox browsers.

      Before using the Vue.js Devtools, you will first have to download the program to your computer. This step will focus on installing Devtools in either a Chromium-based browser or Firefox.

      Note: Before you move on, it’s important to note that the example project that you set up in Step 1 is using Vue.js 3. This means, in order for you to use Vue.js Devtools with this newest version of Vue at the time of publishing this tutorial, you will need to install the Beta channel version of Vue.js Devtools.

      Installing on Chromium Browsers

      If you are using a Chromium browser, you can download the Devtools as a Chrome extension. The more common Chromium browsers are Google Chrome, Microsoft Edge, and Opera. If you are using other browsers such as Brave or Vivaldi, this step will work as well.

      To install, first visit the Chrome Web Store. In the search bar in the top-left, search for “Vue Devtools”. You will find a number of results, as shown in the following image:

      The Chrome Web Store search results for

      Make sure you only install Devtools that are offered by Vue.js or “vuejs-dev”, since these are maintained by the official team and are the most trusted solutions. Since you’re app is using Vue 3, you must download the Beta channel offered by “vuejs-dev”.

      Once you click on that, you can view additional information about this extension, including screenshots and an overview, as shown in the following image:

      Description page in the Chrome Web Store for Vue.js Devtools, showing a screenshot of the tool and a blue

      When you are ready, click on the Add to Chrome button in the top-right of the browser. Your browser will then download and install the extension.

      Installing on Firefox

      If you are using Mozilla Firefox, visit the Firefox Extension Store. In the top-right of your browser you will see a search bar. In it, search for “Vue Devtools”, as shown in the following image:

      Firefox Browser Add-Ons page, with

      This will show a number of search results, including an entry published by Evan You:

      Search results for

      Click on the extension that is from “Evan You”. From here, you can view additional information about the extension, including screenshots and an overview:

      Vue.js Devtools page on the Firefox Add-Ons site, with rating information, a screenshot, and a blue button labeled

      Click on the Add to Firefox button to download the extension and install it into your browser.

      Note: If you are using an unsupported browser, such as Safari for example, you can still use Vue.js Devtools as a standalone Electron application via npm. Vue’s DevTool documentation has a page about installing the Devtools as a standalone app to debug an application locally or remotely.

      You now have the Devtools extension installed to your browser. You will see the Vue.js logo where your pinned extensions show up in your browser bar. By default, this logo will be grayed out. But if you visit an application or website that is using Vue, that logo will become colorized with the Vue logo colors.

      In this section, you successfully installed the Devtools on your Chromium or Firefox browser. In the next section, you are going to take a dive into the user interface of the application to view the properties of your AirportCard components from the browser.

      Next, you will navigate through the Devtools interface to inspect the properties of the components in your sample application.

      In Step 1, you started your project on a local development server at localhost:8080. Navigate to that URL in your browser, then right-click or use CMD+ALT+I on a Mac or CTRL+ALT+I in Windows to open your browser inspection tools. In the top bar of your inspector window, there will be a new tab titled Vue. Click on that tab and the Devtools will initialize:

      The Vue tab highlighted in the inspector window.

      Vue.js Devtools initially has two sections: a listing view and a detail view. The listing view contains a list of Vue components or Vuex namespaces. When the Devtools start up, you will see a list of components on the left. Since there are a total of six airport cards displayed in your sample application, there will be six AirportCard components displayed in the listing view, along with the App component.

      In the listing view, select the first App component. The detail view will now provided extra detail about that component. In this case, you will find all of the data and computed properties associated with App.vue, which in this case includes setup with an array of objects representing the airports in airports.js.

      Now click on the first AirportCard directly under App. You will find all of the data and computed properties associated with that instance of the AirportCard.vue component. In this case, it is the CVG card, so it will show properties like abbreviation: "CVG" and state: KY. When hovering over this component, Devtools will also highlight the rendered component in your browser:

      Vue Devtools with the first component selected in the listing view, the component's properties displayed in the detail view, and the component highlighted in the browser display.

      This section showed some basic navigation of the Devtools interface, allowing you to check the properties of components generated in your Vue app. In the next step, you are going to make some changes to the Vue application via the Devtools. With these small edits, you will see how Devtools can be used to test changes to your components.

      With Vue.js Devtools, you can change the properties of your components from the browser, without needing to change your source code. This allows you to debug your application more quickly, without having to adjust your actual code. In this step, you will try this capability out by adding some conditional styling to your AirportCard components and testing it in the browser.

      Currently in your application, there are six different airports. You are going to add some code that will change the color of the card if the airport is under construction. To do this, you will need to navigate to the AirportCard.vue component. You could navigate to it manually, or you can leverage Vue.js Devtools to open this file in your text editor of choice.

      In the browser, with the Devtools window open, click on the crosshairs in the top-right of the development tool. When that is clicked, you can hover over the various airport cards. Devtools will highlight the component in a green color, indicating that the component is selected. When highlighted, click on the card:

      Vue.js Devtools open, with the

      Now that you have your component selected, click on the icon in the top-right that looks like a square with a diagonal arrow. This icon is typically used to notify the user that something will be opened outside of the application. Clicking this will open AirportCard.vue in your editor. With your component file opened, go ahead and add the following highlighted code:

      favorite-airports/src/components/AirportCard.vue

      <template>
        <div class="airport">
          <p>{{ airport.abbreviation }}</p>
          <p>{{ airport.name }}</p>
          <p>{{ airport.city }}, {{ airport.state }}, Under Construction? {{ airport.construction }}</p>
        </div>
      </template>
      ...
      

      This will render the data within the construction property of the airport object. With that complete, add data to the src/data/airports.js file to show if an airport is under construction or not:

      favorite-airports/src/data/airports.js

      export default [
        {
          name: 'Cincinnati/Northern Kentucky International Airport',
          ...
          construction: false
        },
        {
          name: 'Seattle-Tacoma International Airport',
          ...
          construction: false
        },
        {
          name: 'Minneapolis-Saint Paul International Airport',
          ...
          construction: false
        },
        {
          name: 'Louis Armstrong New Orleans International Airport',
          ...
          construction: false
        },
        {
          name: `Chicago O'hare International Airport`,
          ...
          construction: false
        },
        {
          name: `Miami International Airport`,
          ...
          construction: false
        }
      ]
      

      Save and close this data file.

      With your data added to your application, you will now write some CSS to add a class if the construction property is true. Open the AirportCards.vue component to add the following:

      favorite-airports/src/components/AirportCard.vue

      <template>
        <div class="airport" :class="{ 'is-under-construction': airport.construction }">
          <p>{{ airport.abbreviation }}</p>
          <p>{{ airport.name }}</p>
          <p>{{ airport.city }}, {{ airport.state }}, Under Construction? {{ airport.construction }}</p>
        </div>
      </template>
      
      <style scoped>
      ...
      .airport.is-under-construction {
        border-color: red;
        color: red;
      }
      </style>
      

      On the <div> with the airport class, you are binding an object to the class attribute. The key of this object is the class that will be applied, and the property is the condition that needs to be met before applying the class. The CSS in the <style> tags changes the border and the text color of the card to red if the class is-under-construction is applied to the card.

      Now open your browser and open the Vue.js Devtools. In the left pane, highlight the <App> component. On the right column, the setup method and its properties will be displayed. You will find an array of airports. Expand that array, then expand one of the objects in it. Open the first object with the abbreviation CVG. To test if this code works, click on the pencil icon of the construction data property and change it to true.

      Vue Devtools opened to modify the CVG card to have a true value for construction. The corresponding card in the browser window is red.

      Once you press ENTER or click on the save icon, that data will be updated in the frontend. The card will immediately change to red. If you were to inspect the element and review the DOM, you would find that the is-under-construction class has been applied to the first airport card.

      Now that you’ve tried out changing properties in Devtools to test your application logic, you will next see how to test emitted events from the browser.

      In addition to debugging a component’s data and properties, you can also use Devtools to debug built-in and custom events. Vue.js Devtools provides tools for visualizing events and their payloads.

      To try out testing an event, you will first add an event to your AirportCard component. When you click on an airport card, the app will emit an event to the parent component (App.vue) with the airport data that you clicked.

      Open your text editor, and in the AirportCard.vue component, add the following click event with an $emit on the outermost <div>:

      favorite-airports/src/components/AirportCard.vue

      <template>
        <div class="airport" :class="{ 'is-under-construction': airport.construction }" @click="$emit('favorite-airport', airport)">
          <p>{{ airport.abbreviation }}</p>
          <p>{{ airport.name }}</p>
          <p>{{ airport.city }}, {{ airport.state }}, Under Construction? {{ airport.construction }}</p>
        </div>
      </template>
      ...
      

      On the outermost <div>, you are invoking a JavaScript click event. In that click event, you are firing off a custom event called favorite-airport. The payload for that event is the data associated with the airport variable from the v-for loop.

      Save the changes to your AirportCard component.

      Next, open up Vue.js Devtools in your browser. In the top bar to the right of the compass icon (inspector) is the timeline inspector. Click on the icon to open up the Devtools timeline:

      Animation showing a mouse cursor clicking the event timeline view in Vue.js Devtools.

      This timeline inspector lets you inspect any event, keyboard stroke, mutation, and more that happens over a period of time. With the tools open, click on one of the airport cards in the browser viewport. Once you do that, you will see a series of dots on your timeline:

      Vue Devtools event timeline view showing a series of purple and green dots in lines parallel to each other.

      These dots represent various events that happen behind the scenes, including the mousedown and mouseup events that were executed on your click. Mouse events will always be highlighted in purple. The green dot is your custom event that you set up to fire on the click event. Click on the green dot to find more information regarding that event, as shown in the following image:

      Two extra panes of the Vue.js Devtools timeline view that show a log of component events and information about the event, including its name, component, and parameters.

      In this pane, you can see when the event was executed, as well as the data that was passed up to the parent. In this case, the payload of the event was the Chicago airport with the code ORD. This shows that your event fired correctly, allowing you to continue to use this event to develop additional features. In the next step, you will use this event to store the payload data in a Vuex store to keep track of a user’s favorite airports.

      In Vue.js Devtools, there is a specific pane set aside for monitoring Vuex actions, mutations, and stored state. By using this view, you can inspect your state for problems without having to iterate through source code changes. In this section, you will implement Vuex to make a list of favorite airports that a user can add to by clicking the airport card. You will then monitor these features with Vue.js Devtools.

      With your custom event created and executing, you will now take that data and call a Vuex action and mutation with it. If you are not familiar with Vuex, it is recommended to read more about it in our Vuex tutorial.

      In your text editor, open the src/store/index.js file and add the following code:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
        state: {
          favorites: [],
        },
        mutations: {
          UPDATE_FAVORITES(state, payload) {
            state.favorites = payload
          }
        },
        actions: {
          addToFavorites({ state, commit }, payload) {
            const airports = state.favorites
            airports.push(payload)
            commit('UPDATE_FAVORITES', airports)
          }
        }
      })
      

      In this snippet, you are adding to the index.js file that was generated in Step 1. Specifically, you are adding a favorites state property (an array of airports.js objects), a mutation called UPDATE_FAVORITES, and an action called addToFavorites. The intent is for the user to click on a card, which will fire the action, which will fire the mutation, which will then update the store.

      In your App.vue component, add the following code to dispatch an action when your custom favorite-airport event is executed:

      favorite-airports/src/App.vue

      <template>
        <div class="wrapper">
          <div v-for="airport in airports" :key="airport.abbreviation">
            <airport-card :airport="airport" @favorite-airport="$store.dispatch('addToFavorites', $event)"/>
          </div>
        </div>
      </template>
      
      <script>
      import { ref } from 'vue'
      import allAirports from '@/data/airports.js'
      import AirportCard from '@/components/AirportCard.vue'
      
      export default {
        components: {
          AirportCard
        },
        setup() {
          const airports = ref(allAirports)
          return { airports }
        }
      }
      </script>
      ...
      

      With your Vuex properties added, open your browser and open the Devtools. In the Components dropdown at the top of the pane, you will now find a Vuex option. Click to switch to the Vuex view.

      The Vuex dropdown in the Vue Devtools

      Similar to the Components section, the left panel is a visualization of your state tree, complete with namespaced modules and any root state that you might have. To the right is a full list of data in that module or section of your Vuex state.

      In the right panel, you will find an empty array of favorites. Now click on a few cards and see each airport object get pushed into that array:

      Animation showing airports being added to a favorites list in the Vuex store.

      Now that you know that the state is working, you can expand on this by adding an additional section to the App.vue file:

      favorite-airports/src/App.vue

      <div class="wrapper">
          <div v-for="airport in airports" :key="airport.abbreviation">
            <airport-card :airport="airport" @favorite-airport="$store.dispatch('addToFavorites', $event)"/>
          </div>
      
        <div v-if="$store.state.favorites.length">
          <h1>Favorites <template v-if="$store.state.favorites.length">({{ $store.state.favorites.length }})</template></h1>
          <div v-for="airport in $store.state.favorites" :key="airport.abbreviation">
            <airport-card :airport="airport" />
          </div>
        </div>
      </div>
      ...
      

      Save the file, then click on a card in your browser. A new section will be displayed with cards of the airports that have been added.

      The Vue application rendered in a browser, with a list of favorite airports beneath the airport cards.

      Conclusion

      Vue.js Devtools is a free browser extension that you can add to any Chromium or Firefox broswer. You can also download it and as a standalone application that can be tied to your application. In this tutorial, you built an application from a dataset that rendered a number of card components, each with their own data. You then used Devtools to examine the properties of the components, change properties to test conditional style changes, test to see if events fired appropriately, and ensured that Vuex state management was set up correctly.

      To learn more about Vue.js Devtools, you can visit the official Vue DevTools documentation. For more tutorials on Vue, check out the How To Develop Websites with Vue.js series page.



      Source link

      How To Manage State in a Vue.js Application with Vuex


      The author selected Open Sourcing Mental Illness to receive a donation as part of the Write for DOnations program.

      Introduction

      Vuex is the first-party development state management library for Vue.js. It was created by Evan You and is currently maintained by the Vue.js Core Team. Like many other state management libraries, Vuex follows the principle that Redux has popularized over the past years: Data flows in one direction, with actions and mutations modifying data in a single source of truth called the store.

      A Vuex store is a collection of different methods and data. Some of these methods, such as actions, can fetch and process data before it is sent to a mutation. A mutation is a method that mutates or updates the store property with the value provided. Getters are methods that can modify or combine data to create a new state property. These getters are read-only and do not mutate data. These are similar to computed properties in a Vue.js component. The last component to Vuex is the state, or the dataset that acts as your single source of truth.

      In this tutorial, you will create an application that renders a list of cards with airport information in them. When clicked, these cards will execute the Vuex workflow to add the selected airport to a list of favorites. By running through this example, you will make actions and mutations to manage state and getters to retrieve computed data.

      Prerequisites

      Step 1 — Setting Up the Example Application

      In order to help visualize how state is managed with Vuex, set up a project with some data to display in the view. You will use this project and throughout the tutorial.

      Once the favorite-airports project is created as described in the Prerequisites section, create a directory to hold all of your local data for this project. Open your terminal and run the following commands in the project root directory (favorite-airports):

      • mkdir src/data
      • touch src/data/airports.js

      This will create the data directory and an empty airports.js file inside it.

      In your text editor of choice, open the newly created airports.js file and add in the following:

      favorite-airports/src/data/airports.js

      export default [
        {
          name: 'Cincinnati/Northern Kentucky International Airport',
          abbreviation: 'CVG',
          city: 'Hebron',
          state: 'KY'
        },
        {
          name: 'Seattle-Tacoma International Airport',
          abbreviation: 'SEA',
          city: 'Seattle',
          state: 'WA',
        },
        {
          name: 'Minneapolis-Saint Paul International Airport',
          abbreviation: 'MSP',
          city: 'Bloomington',
          state: 'MN',
        },
        {
          name: 'Louis Armstrong New Orleans International Airport',
          abbreviation: 'MSY',
          city: 'New Orleans',
          state: 'LA',
        },
        {
          name: `Chicago O'hare International Airport`,
          abbreviation: 'ORD',
          city: 'Chicago',
          state: 'IL',
        },
        {
          name: `Miami International Airport`,
          abbreviation: 'MIA',
          city: 'Miami',
          state: 'FL',
        }
      ]
      

      This is an array of objects consisting of a few airports in the United States. In this application, you are going to iterate through this data to generate cards consisting of the name, abbreviation, city, and state properties. When the user clicks on a card, you will execute a dispatch method, which will add that airport to your Vuex state as a favorite airport.

      Save data/airports.js and return to the terminal.

      When you’ve completed that step, create a single-file component (SFC) with the name AirportCard.vue. This file will live in the components directory of your project. This component will contain all the styles and logic for the airport card. In your terminal, create the .vue file using the touch command:

      • touch src/components/AirportCard.vue

      Open AirportCard.vue in your text editor and add the following:

      favorite-airports/src/components/AirportCard.vue

      <template>
        <div class="airport">
          <p>{{ airport.abbreviation }}</p>
          <p>{{ airport.name }}</p>
          <p>{{ airport.city }}, {{ airport.state }}</p>
        </div>
      </template>
      
      <script>
      export default {
        props: {
          airport: {
            type: Object,
            required: true
          }
        }
      }
      </script>
      
      <style scoped>
      .airport {
        border: 3px solid;
        border-radius: .5rem;
        padding: 1rem;
        margin-bottom: 1rem;
      }
      
      .airport p:first-child {
        font-weight: bold;
        font-size: 2.5rem;
        margin: 1rem 0;
      }
      
      .airport p:last-child {
        font-style: italic;
        font-size: .8rem;
      }
      </style>
      

      You may notice that there is some CSS included in this code snippet. In the AirportCard.vue component, the wrapper <div> contains the class of airport. This CSS adds some styling to the generated HTML by adding borders to give each airport the appearance of a “card”. The :first-child and :last-child are pseudo selectors that apply different styling to the first and last p tags in the HTML inside the div with the class of airport. In addition to that, you may also notice that this component contains a prop, which in Vue.js is a way to pass data down from a parent component to a child component.

      Save and exit from the file.

      Before wrapping up the setup, replace the existing App.vue component with the following code:

      favorite-airports/src/App.vue

      <template>
        <div class="wrapper">
          <div v-for="airport in airports" :key="airport.abbreviation">
            <airport-card :airport="airport" />
          </div>
        </div>
      </template>
      
      <script>
      import { ref } from 'vue'
      import allAirports from '@/data/airports.js'
      import AirportCard from '@/components/AirportCard.vue'
      
      export default {
        components: {
          AirportCard
        },
        setup() {
          const airports = ref(allAirports)
          return { airports }
        }
      }
      </script>
      
      <style>
      #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      
      .wrapper {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        grid-column-gap: 1rem;
        max-width: 960px;
        margin: 0 auto;
      }
      
      p,
      h3 {
        grid-column: span 3;
      }
      </style>
      

      This code contains a v-for loop that iterates through the airports.js data and renders a series of AirportCards.vue components with airport data passed in via the prop :airport. Save this code and return to the command line.

      With the project set up, run the local development server using the npm run serve command in your terminal:

      This will start a server on your localhost, usually at port 8080. Open your web browser of choice and visit localhost:8080to see the following:

      A list of cards rendered in Vue using the v-for directive.

      Now that your example application is set up, in the next step you are going to install the Vuex library and create a store. This store is a collection of a number of different Vuex items including: state, mutations, actions, and getters. To illustrate this, you will be executing dispatch methods, which will add an airport to a favorites section of your app.

      Step 2 — Installing Vuex

      When working on web-based applications, you will often work with state. State is a collection of data at a given time. This state can be changed with user interactions via dispatch and commit methods. When the user modifies data, a dispatch event is executed, which passes data to a mutation and updates the state object.

      There are a few ways to approach updating state. Some developers skip actions and go straight to mutatations. However, for the sake of this tutorial, you will always execute an action that in turn calls a mutation. This way you can have multiple mutations inside of an action. The cardinal rule of Vuex is that mutations have one job and one job only: update the store. Actions can do a number of different things including combining data, fetching data, and running JavaScript logic.

      In addition to actions, there are also getters. A getter is a way to combine multiple state values into a single value. If you are familiar with computed properties in Vue.js, getters can be thought of as state-specific computed properties.

      With the Vuex terminology covered, start installing and integrating Vuex. Open your terminal and run the following command:

      • npm install vuex@next --save

      This command will install the version of Vuex that is the most compatible with Vue.js 3.x and saves it in your package.json file. Next, create a directory and an index file for your store. You will use the mkdir command to make a directory and touch to create a new file:

      • mkdir src/store
      • touch src/store/index.js

      Open your text editor and in your store/index.js file, initialize your Vuex store. To do this, you need to leverage the createStore function from Vuex:

      airport-favorites/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
      
      })
      

      You also export this, as you will later import it into your main.js file.

      At this point, you have your Vuex store set up, but the application is not yet aware of it or how to use it. To fully initialize the store, import it into your main.js file. In your text editor, open the src/main.js file.

      Immediately after createApp(App), chain the use method and pass into the store that you are importing, as shown in the following highlighted code:

      favorite-airports/src/main.js

      import { createApp } from 'vue'
      import App from './App.vue'
      import store from './store'
      
      createApp(App).use(store).mount('#app')
      

      Once you’ve chained the use method, save this file. The use method tells the Vue application which code to bundle together when the application is built. In this case, you are telling Vue to “use” or bundle the Vuex store.

      Before you move on to the next section, add a state value into your store and reference it in the App.vue file. Open your store/index.js file and add the following objects and values:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
      state: {
          firstName: 'John',
          lastName: 'Doe'
        },
      mutations: {
      
      },
      actions: {
      
      },
      getters: {
      
      }
      })
      

      These properties reflect the type of data the store holds: state for state (global data), mutations (commits that mutate data), actions (dispatches that call mutations), and getters (store computed properties).

      Save store/index.js, then open your App.vue file in your text editor and add the following:

      favorite-airports/src/App.vue

      <template>
        <div class="wrapper">
          <p>{{ $store.state.firstName }} {{ $store.state.lastName }}</p>
          <div v-for="airport in airports" :key="airport.abbreviation">
            <airport-card :airport="airport" />
          </div>
        </div>
      </template>
      ...
      

      The $store in this case is the global store that you initialized in the main.js file. If you were to log this.$store into the console, you would see the store object. From there, the code accesses the property you want to display via dot notation.

      Save App.vue then open your web browser. Above the airport cards, you will see the first and last name that you saved to your Vuex store. These are the default values of firstName and lastName, respectively.

      Data from the Vuex store displayed in the view.

      In this step, you installed Vuex and created a Vuex store. You added some default store data and displayed it in the view with the $store object using dot notion. In the next step, you will be updating your Vuex store via actions and mutations, and you will get combined data with getters.

      Step 3 — Creating Actions, Mutations, and Getters

      In Step 2, you installed Vuex manually and integrated it into your project. In this step you still have the first and last name rendered in your browser, but you will create a Vuex getter to render the data as one string. As mentioned before, you can think of Vuex getters as computed properties for your Vuex store.

      To create a getter, open your src/store/index.js file in your text editor of choice. Once open, create a property in the getters object with a function as its value. The name of the property is how you will access the getter later.

      Add the following highlighted code:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
        state: {
          firstName: 'John',
          lastName: 'Doe'
        },
        ...
        getters: {
          fullName: function () {
      
          }
        }
      })
      

      In this case, you will use the function to combine the first and last names and store the resulting property as fullName. Inside the function, you will need to pass in the state object that is inside your Vuex store. From there, return a string with the first and last name interpolated:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
        state: {
          firstName: 'John',
          lastName: 'Doe'
        },
        ...
        getters: {
          fullName: function (state) {
            return `${state.firstName} ${state.lastName}`
          }
        }
      })
      

      You are using template literals here to put the firstName and lastName into one string.

      Save this file, then move back to App.vue. In this file, remove the first and last values and replace them with the getter:

      favorite-airports/src/App.vue

      <template>
        <div class="wrapper">
          <p>{{ $store.getters.fullName }}</p>
          <div v-for="airport in airports" :key="airport.abbreviation">
            <airport-card :airport="airport" />
          </div>
        </div>
      </template>
      ...
      

      Once you make this change and save the file, your browser will hot reload. You will see the first and last name in your browser as before, but now you are leveraging the getter. If you change one of the names in your Vuex store, the getter will be updated automatically.

      Moving on from getters, there are actions. As mentioned in the last step, for the sake of this tutorial, you will always use an action rather than mutating the data directly.

      In this project, you will add an airport’s data to a “favorites” list when a user clicks on the card. You are going to first get the action and mutation created, then later assign it to a click event using the v-on directive.

      To create an action, open the src/store/index.js file in your text editor. In the actions section of the store, create a function. Like the getter, the function name will be how you reference the action later. Name this function addToFavorites:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
        state: {
          firstName: 'John',
          lastName: 'Doe',
          favorites: [] // will store favorites here
        },
        mutations: {
      
        },
        actions: {
          addToFavorites() {
      
          }
        },
        getters: {
          fullName: function (state) {
            return `${state.firstName} ${state.lastName}`
          }
      }
      })
      

      An action accepts two arguments: the context, or the Vue app itself, and the payload, or the data that you want to add to the store. The context has a commit method associated with it that you will use to call a mutation you will make later:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
        state: {
          firstName: 'John',
          lastName: 'Doe',
          favorites: []
        },
        mutations: {
      
        },
        actions: {
          addToFavorites(context, payload) {
            context.commit('UPDATE_FAVORITES', payload)
          }
        },
        getters: {
          fullName: function (state) {
            return `${state.firstName} ${state.lastName}`
          }
       }
      })
      

      The commit method also accepts two arguments: the name of the mutation to call and the payload or the data that the mutation will replace the state with.

      In this code, you are naming the mutation UPDATE_FAVORITES. Mutation names should be agnostic and not named after a specific action. For example, a mutation like ADD_FAVORITE and REMOVE_FAVORITE implies a logic, like removing or adding a piece of data. This is not ideal, since mutations should have one job and one job only: update the state. To differentiate between adding and removing data, you could have two different actions that remove or add a favorite airport from the array, which then execute a single mutation called UPDATE_FAVORITES that updates the array with whatever was passed in. Minimizing the amount of mutations you have in your store will help make your Vuex store easier to manage as it grows larger in complexity and size.

      Next, add some logic to this action. When you add an airport as a “favorite”, you will add that payload (the airport data) to the existing array. To do that, you can use the push method in JavaScript:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
        state: {
          firstName: 'John',
          lastName: 'Doe',
          favorites: []
        },
        mutations: {
      
        },
        actions: {
          addToFavorites(context, payload) {
            const favorites = context.state.favorites
            favorites.push(payload)
            context.commit('UPDATE_FAVORITES', favorites)
          }
        },
        getters: {
          fullName: function (state) {
            return `${state.firstName} ${state.lastName}`
          }
       }
      })
      

      At this point, your action is set up to to add the payload to your favorites array then call a mutation with the mutated array as the new data. Next, you will define the UPDATE_FAVORITES mutation. Add the following code to set the favorites array as a new array:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
        state: {
            firstName: 'John',
          lastName: 'Doe',
          favorites: []
          },
        mutations: {
          UPDATE_FAVORITES(state, payload) {
            state.favorites = payload
          }
        },
        actions: {
          addToFavorites(context, payload) {
            const favorites = context.state.favorites
            favorites.push(payload)
            context.commit('UPDATE_FAVORITES', favorites)
          }
        },
        getters: {
          fullName: function (state) {
            return `${state.firstName} ${state.lastName}`
          }
       }
      })
      

      Now that you have your action and your mutation, you can save this file.

      To execute this action, you can call a dispatch event when the user clicks on a card. You will do this with the v-on directive.

      Open the App.vue file in your text editor. On the <airport-card /> component, add the v-on directive shorthand syntax (@) with the event being click:

      favorite-airports/src/App.vue

      <template>
        <div class="wrapper">
          <p>{{ $store.getters.fullName }}</p>
          <div v-for="airport in airports" :key="airport.abbreviation">
            <airport-card :airport="airport" @click="$store.dispatch('addToFavorites', airport)" />
          </div>
          <h2 v-if="$store.state.favorites.length">Favorites</h2>
          <div v-for="airport in $store.state.favorites" :key="airport.abbreviation">
            <airport-card :airport="airport"  />
          </div>
        </div>
      </template>
      ...
      

      The dispatch function accepts two arguments: the action name and the payload data that you are sending to the action.

      Save this file and open it in your browser. Now, when you click on an airport card, the action will call the mutation that updates the state and adds the airport to the favorites property.

      A favorite airport added to the favorite airports section after a Vuex mutation was executed.

      In this step, you expanded on the Vuex store that you created earlier. You created an action that copies an array and pushes a new item to that array. That action called a mutation that in turn updated the state. In addition to that, you learned about getters and how they can be leveraged to create new properties by combining or modifying read-only values in the Vuex store.

      In the final step, you will implement Vuex modules. Modules are a great way to break up your Vuex store into smaller Vuex stores. This is useful as your Vuex store growers larger in size and complexity.

      Step 4 — Composing Vuex Modules

      Modules are smaller Vuex stores that are combined into a single Vuex store. This is similar to how multiple Vue.js components are imported into a single .vue file, such as App.vue. In this step, you are going to separate this Vuex store into two separate modules. One module will be for a user state, and the other will be specific to airport state, actions, and mutations.

      In your terminal, cd into the store directory and use the touch command to create two separate files.

      • touch src/store/user.module.js
      • touch src/store/airports.module.js

      Inside of the user.module.js file, create an object that will be exported by default by adding the following code:

      favorite-airports/src/store/user.module.js

      export default {
        namespaced: true
      }
      

      You are also adding the property namespaced with its value as true. The namespace property will make it so you can reference the module name while accessing a property with dot notation later.

      Inside of this object, you will add the state and getter information that is associated with a user:

      favorite-airports/src/store/user.module.js

      export default {
        namespaced: true,
        state: {
          firstName: 'John',
          lastName: 'Doe'
        },
        getters: {
          fullName: function (state) {
            return `${state.firstName} ${state.lastName}`
          }
        }
      }
      

      The user module contains everything that you need for user information. Save and exit from the file.

      Go ahead and do the same thing in the airports.module.js file. Open the airports.module.js file in your text editor and add the following:

      favorite-airports/src/store/airports.module.js

      export default {
        state: {
          favorites: []
        },
        mutations: {
          UPDATE_FAVORITES(state, payload) {
            state.favorites = payload
          }
        },
        actions: {
          addToFavorites(context, payload) {
            const favorites = context.state.favorites
            favorites.push(payload)
            context.commit('UPDATE_FAVORITES', favorites)
          }
        },
      }
      

      Now that you have put the airport-related mutations, actions, and state, you can save your airports.module.js.

      Next, import these two files into the main store/index.js file. In your text editor, open the store/index.js file and remove the state, mutations, actions, and getters properties. Your file will resemble the following snippet:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      
      export default createStore({
      
      })
      

      To register modules, you will need to import them into this index.js file with the following highlighted code:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      import UserModule from './user.module.js'
      import AirportsModule from './airports.module.js'
      
      export default createStore({
      
      })
      

      From here, you will need to have a property called modules with an object as its value. The property names inside of this object will be the names of the Vuex modules. The value of the module name is the imported module itself:

      favorite-airports/src/store/index.js

      import { createStore } from 'vuex'
      import UserModule from './user.module.js'
      import AirportsModule from './airports.module.js'
      
      export default createStore({
      modules: {
        user: UserModule,
        airports: AirportsModule
      }
      })
      

      Once you save this file, your modules have now been registered and combined into your single Vuex store. Save store/index.js, then open the App.vue file and update it to reference the newly created modules:

      favorite-airports/src/App.vue

      <template>
        <div class="wrapper">
          <p>{{ $store.getters['user/fullName'] }}</p>
          <div v-for="airport in airports" :key="airport.abbreviation">
            <airport-card :airport="airport" @click="$store.dispatch('addToFavorites', airport)" />
          </div>
          <h2 v-if="$store.state.airports.favorites.length">Favorites</h2>
          <div v-for="airport in $store.state.airports.favorites" :key="airport.abbreviation">
            <airport-card :airport="airport"  />
          </div>
        </div>
      </template>
      ...
      

      Now you have a modular version of your Vuex setup.

      In this step, you segmented your existing Vuex store into smaller chunks called modules. These modules are a great way to group related store properties into a smaller Vuex store. You also updated your App.vue to reference the state and dispatch events in each module.

      Conclusion

      At a high level, state management is all about updating data. In this setup, the data in the state is global throughout your entire application and acts as a single source of truth, which can only be updated with explicit functions in the form of actions and mutations. In this tutorial, you ran through examples of state, mutations, actions, and getters and saw how each of these properties have their own purpose in the update cycle.

      To learn more about Vuex, actions, mutations, and modules, review the official Vuex documentation written by the Vue.js Core Team. For more tutorials on Vue, check out the How To Develop Websites with Vue.js series page.



      Source link

      How To Use Links and Buttons with State Pseudo-Classes in CSS


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

      Introduction

      An important part of web development is providing feedback when a user interacts with an element. This interaction is accomplished through changing state, which indicates how the user is using or has used a given element on the page. In CSS, there are special variations on selectors called a pseudo-class, which allow state changes to initiate style changes.

      In this tutorial, you will use the :hover, :active, and :focus user actions and the :visited location pseudo-classes. You will use <a> and <button> as the interactive elements in the tutorial. Both of these elements have similar interactive states and are functionally identical to the user. From a development standpoint, <a> elements are specifically for interacting with URLs, whereas the <button> element is used to trigger forms or JavaScript functions. In addition to working with these four distinct states, you will use the transition property to animate the styles between these states of interactivity.

      Prerequisites

      Setting Up the Initial HTML and CSS

      To begin working with links and buttons, you will first set up the HTML and CSS needed as a foundation for the tutorial. In this section, you will write out all the necessary HTML and some initial CSS styles that will handle layout and start the visual aesthetic.

      To begin, open index.html in your text editor. Then, add the following highlighted HTML to the file:

      index.html

      <!doctype html>
      <html>
        <head>
        </head>
        <body>
        </body>
      </html>
      

      Next, go to the <head> tag and add a <meta> tag to define the character set for the HTML file. Then set the title of the page, add a <meta> tag defining how mobile devices should render the page, and finally load the CSS file with a <link> tag. These additions are highlighted in the following code block. You will encounter this highlighting method throughout the tutorial as code is added and changed:

      index.html

      <!doctype html>
      <html>
        <head>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>Link and Buttons with State</title>
          <link rel="stylesheet" href="https://www.digitalocean.com/community/tutorials/styles.css">
        </head>
        <body>
        </body>
      </html>
      

      After adding the <head> content, next move to the <body> element where content is added to make an informational block with <a> and <button> tags as interactive elements. Add the highlighted section from this code block to your index.html file in your text editor:

      index.html

      <!doctype html>
      <html>
        <head>
          <meta charset="utf-8">
          <meta name="viewport" content="width=device-width, initial-scale=1">
          <title>Link and Buttons with State</title>
          <link rel="stylesheet" href="https://www.digitalocean.com/community/tutorials/styles.css">
        </head>
        <body>
          <section>
            <header>
              <button class="link" type="button">
                Close Window
              </button>
            </header>
            <div>
              <p>
                This is a demo paragraph for some demo content for a tutorial. By reading this text you are now informed that this is text for the demo in <a href="https://do.co/tutorials">this tutorial</a>. This means you can agree to use this content for the demo or not. If you wish to continue with this tutorial demo content, please select the appropriately styled interactive element below.
              </p>
              <div>
                <button class="button" type="button">
                  Yes, Please
                </button>
                <a class="button" href="https://www.digitalocean.com/community/tutorials/#">
                  No, Thank you
                </a>
              </div>
            </div>
          </section>
        </body>
      </html>
      

      Save your changes to index.html and open your web browser to open the index.html file there. The content of the page will appear in a black serif font on a white background. The <button> element styles will vary based on your browser and operating system, but the page will look similar to the following image:

      Black serif text on a white background with two blue underlined links and two interactive butons.

      Next, create a file called styles.css in the same directory as the index.html file. The following styles in the code block will set up a base style for the container holding the <button> and <a> elements you will be styling throughout the remainder of the tutorial. Add the following code to your styles.css file:

      styles.css

      body {
        background-color: #454545;
        color: #333;
        font-family: sans-serif;
        line-height: 1.5;
      }
      
      section {
        margin: 2rem auto;
        width: 90%;
        max-width: 50rem;
        box-sizing: border-box;
        padding: 2rem;
        border-radius: 2rem;
        border: 0.25rem solid #777;
        background-color: white;
      }
      
      header {
        text-align: right;
      }
      

      The CSS in this code block adds styles for three portions of the demo content area. The first is the body selector, which applies a dark gray background and then defines default font properties. The second selector focuses on the <section> element, which is the primary container for the demo content and creates a white block with big, rounded corners and a maximum width so it will only grow to a specified amount. Lastly, the header element selector sets the text alignment to the right so the Close Window button is to the far top right corner of the <section> container.

      If you’d like to learn more about how to use CSS element selectors, check out How To Select HTML Elements to Style with CSS.

      Save your changes to the styles.css file and reload the index.html file in your browser. The page styles will have transformed from the browser defaults to a customized style, as demonstrated in the following image:

      Black sans serif text in a white container with rounded corners with two blue underlined links and two interactive butons.

      You have set up your HTML and loaded in the base styles for the contents of the page. Next you will create a custom default link style and provide a way for that default style to be applied to button elements.

      In this section, you will create a custom default link style by setting a new color. Then you will remove default button styles in order to make the button look like a default link. a and button have distinct purposes, but website users interact with both in a similar way. Sometimes, these two elements need to appear similar to help explain an interaction to the user or match a design aesthetic.

      For the first part of this section, you will set up the default link style that will be used on the generic <a> element and a class of .link, which can then apply the generic link styles to the <button> element. Start by creating a group selector containing an a element selector and a .link class selector with a color property and value of #25a:

      styles.css

      ...
      a,
      .link {
        color: #25a;
      }
      

      Save your changes to styles.css and open index.html in your browser. The <a> elements on the page are now a deeper blue color than the browser default link blue. Also, the <button> element with the class="link" has the same blue color text in the button. The appearance of change in the browser is demonstrated in the following image:

      Black sans serif text in a white container with rounded corners with two blue underlined links and two interactive buttons.

      Next, begin removing the default appearance of a button. By default, browsers add a lot of customization to the look and feel of <button> elements to make sure that they are distinguishable as interactive elements. To remove the extra styling that browsers add to these elements, return to styles.css in your text editor, create a button element selector, and add two similar properties as shown in the following code block:

      styles.css

      ...
      a,
      .link {
        color: #25a;
      }
      
      button {
        -webkit-appearance: none;
        appearance: none;
      }
      

      The first property is -webkit-appearance: none;, which is known as a vendor prefix property. The -webkit- position of the property is only read by browsers built on a derivative of the WebKit browser engine, such as Safari or Chrome. Some versions of these browsers don’t support the appearance property by themselves and need the -webkit- prefix in order to work.

      Vendor prefix usage is on the decline, but still occurs. It is important to put any vendor prefixed properties before the non-prefixed to avoid overriding properties on browsers that support both the prefixed and non-prefixed variants.

      Save your changes to styles.css and refresh index.html in your browser. The button element will not lose all its styles; instead, it will become simplified to default styles expected of the web specification. The following image demonstrates how this will appear in the browser:

      Black sans serif text in a white container with rounded corners with two blue underlined links and two interactive buttons.

      To remove the remaining default styles of the button, you will need to add several more properties. Return to your styles.css file in your text editor and go to the button selector. Next, you will add the properties to remove the background-color, border, margin, and padding. Then you’ll remove the properties for the button element, color, font, and text-align, to inherit the value of the page.

      The following code block demonstrates how to set this up:

      styles.css

      ...
      button {
        -webkit-appearance: none;
        appearance: none;
        background-color: transparent;
        border: none;
        margin: 0;
        padding: 0;
        color: inherit;
        font: inherit;
        text-align: center;
      }
      

      Save these changes to styles.css and refresh index.html in your web browser. Both buttons have now lost their default styles, with the Close Window button being closer in styles to the link. The Yes, Please button styles will be addressed in the next section. The following image demonstrates how this will appear in the browser:

      Black sans serif text in a white container with rounded corners with two blue underlined links and two interactive buttons that appear as plain text.

      To finish making the Close Window button look like a text link, open styles.css in your text editor. Below the a, .link group selector, add a new class selector for only the .link class. Add to that a text-decoration property with a value of underline. Then add a property called cursor, which defines how the mouse cursor appears when on that element, and set the value to pointer. The pointer value styles cursor is the hand-style that appears over a link by default:

      styles.css

      ...
      a,
      .link {
      ...
      }
      
      .link {
        text-decoration: underline;
        cursor: pointer;
      }
      ...
      

      Save these changes to your styles.css file and then return to your browser and refresh index.html. The Close Window button will now appear and behave in a similar manner to the generic <a> element styles. The following animation demonstrates how the cursor changes when moving over the Close Window button:

      A button styled to look like a defaul link with blue text and underlined.

      In this section, you created a custom default style for the <a> element and created a .link class to apply link styling to a <button> element. In the next section, you will create a custom, button-like style that can be applied to both <button> and <a> elements.

      Creating Styles for a Button

      Next, you will create a custom, button-like style with a class selector so the styles can be used on either a <button> or an <a> element. Unlike <a> elements, which are used throughout text content, the <button> element has a more intentional purpose. This makes it less necessary to create generic styles for the <button> element, and instead allows you to always add a class attribute.

      Start by opening up styles.css in your text editor. Create a new class selector called .button. The styles here will redefine many of the properties that were reset in the previous step on the button element selector. You will add color to the text with the color property, fill the shape with the background-color property, then provide some definition with the border property. Afterward you will give a rounded corner to the button with the border-radius property. Finally, you will use the padding shorthand to give space above and below the text, then double that amount on the left and right.

      The following code block contains these values:

      styles.css

      ...
      .button {
        color: #25a;
        background-color: #cef;
        border: 1px solid #67c;
        border-radius: 0.5rem;
        padding: 0.75rem 1.5rem;
      }
      

      Save your changes to styles.css and return to your browser to refresh the index.html file. The look of the Yes, Please and No, Thank you buttons will change to match the properties. The text is the same blue as the default a style, with a much lighter blue in the background, and another deep blue for the border. The next image demonstrates how this will appear in the browser:

      Text with two buttons below with one button taller than the other. The shorter button has underlined text.

      There is a noticeable difference in size between the two buttons. Since the No, Thank you button is using an <a> element instead of a <button>, there are a few additional properties that need to be added to the .button selector to equalize the defaults between these two elements.

      Return to styles.css in your text editor and go to the .button class selector. To begin, add a display: inline-block, which is the default style on button elements. Next, add a text-decoration: none to remove the underline from the <a> element. As you did with the .link selector, add a cursor: pointer to the selector to get the pointing hand icon when a mouse cursor is over the element. Lastly, add a vertical-align: bottom rule. This last property isn’t necessary for all browsers, but it defines where the bottom of elements are positioned on a line:

      styles.css

      ...
      .button {
        color: #25a;
        background-color: #cef;
        border: 1px solid #67c;
        border-radius: 0.5rem;
        padding: 0.75rem 1.5rem;
        display: inline-block;
        text-decoration: none;
        cursor: pointer;
        vertical-align: bottom;
      }
      

      Save these additions to styles.css and then refresh index.html in your browser. The two buttons are now equivalent in visual appearance and have borrowed default attributes from one another, so they are perceived to have a similar interaction.

      Text with two light blue button of equal height with dark blue text and a dark blue thin border.

      You created a custom class selector to style both <button> and <a> elements with a button-like style in this section. Next, you will create a conditional style when a mouse cursor is on top of the interactive elements.

      Now you will use the :hover pseudo-class to create an alternative style that displays when the cursor is on the element. Pseudo-classes are a special group of conditions that are defined by a colon (:) and the name of the condition appended to the selector. For example, the a element selector with a hover pseudo-class becomes a:hover.

      Open styles.css in your text editor. Below the group selector for a, .link, add a new selector for the hover state by appending each selector with the :hover pseudo-class: a:hover, .link:hover. Then, to make a noticeable change when the cursor hovers over the element, add a color property with a value of #b1b, which is a dark pink color:

      styles.css

      ...
      a,
      .link {
        ...
      }
      
      a:hover,
      .link:hover {
        color: #b1b;
      }
      ...
      

      Save the changes to your styles.css file and refresh index.html in your browser. Hover over either the this tutorial link or the Close Window button to initiate the color change on hover. The following image shows what the hover state looks like when the cursor is over the this tutorial link.

      A paragraph of text with a link with a hand cursor icon over the link. The link text is a dark pink color with an underline.

      Next, to add a hover state to the .button elements, return to styles.css in your text editor. Below the .button class selector, add a .button:hover selector to create styles specifically for hover interaction. Next, within the selector, add color properties that will change the button appearance when the cursor is on the buttons. Set a color property to white, then create a background-color and a border-color with both properties set to #25a:

      styles.css

      ...
      .button {
        ...
      }
      
      .button:hover {
        color: white;
        background-color: #25a;
        border-color: #25a;
      }
      

      Save these changes to your styles.css file and return to your browser to refresh the index.html file. Now, take your mouse cursor and hover over one of the two buttons on the bottom. The styles change from the light blue background with a deep blue text and border to a deep blue background with white text. The following image shows what the hover style looks like when the mouse cursor is over the Yes, Please button.

      Two buttons below a paragraph of text. One button has a hand pointer cursor over it and is a dark blue with white text. The other button is light blue with dark blue text.

      You used the :hover pseudo-class in this section to create style changes to the element based on the cursor being positioned on top of the element. In the next section, you will apply this same concept to a condition when a keyboard is used to navigate through the page.

      Applying the :focus Pseudo-Class

      Instead of using a mouse or a touch screen, website visitors will sometimes use their keyboard to navigate and interact with elements of a page. This is most often done by using the TAB key, which will cycle through the interactive elements on the page. The default style uses the outline property to give a visual indicator that the element has focus. This style can be customized by using the :focus pseudo-class to apply property values for this situation.

      To begin working with the focus state of the elements on the page, open your styles.css file in your text editor. Start with a new selector below the a:hover, .link:hover group selector with a new group selector for the focus state: a:focus, .link:focus.

      The most important part of customizing the focus state is to make sure it is noticeably different from the default state. Here, you will make the :focus pseudo-class styles have black text with a gold background:

      styles.css

      ...
      a:hover,
      .link:hover {
        ...
      }
      
      a:focus,
      .link:focus {
        color: black;
        outline: 2px solid gold;
        background-color: gold;
      }
      ...
      

      In this case, you set the color property to black and the background-color property to gold. You also used the outline property, which added some gold color around the edges of the text, outside where the background-color property can reach.

      The outline property works similar to the border shorthand property, as it accepts a width, a style, and a color. However, unlike the border properties, outline always goes around the whole element and can not be set to a specific side. Also, unlike border, the outline does not affect the box-model; the shape is only applied visually and does not change the flow of content.

      Save your changes to styles.css and refresh index.html in your web browser. Begin pressing the TAB key until the browser brings focus to the Close Window and this tutorial elements highlight with a gold background. The following image shows what the this tutorial link looks like in the browser when it has focus:

      A paragraph of text with a link. The link text is black color with an underline and a yellow background.

      Next, to apply a similar custom focus style to the .button class elements, begin by creating a .button:focus class and pseudo-class selector. Since the .button element is already using a border, you will use that to indicate focus, and so remove the outline default by setting the property to have a value of none. Like the link before, the color property will be set to black and the background-color property will be set to gold. Last, set the border-color property to have a value of black:

      styles.css

      ...
      .button:hover {
        ...
      }
      
      .button:focus {
        outline: none;
        color: black;
        background-color: gold;
        border-color: black;
      }
      

      Be sure to save your additions to styles.css and then return to you browser to refresh your index.html file. Again, using the TAB key, cycle through the keyboard-focusable elements on the page until you reach the .button elements. They will now light up with a gold background and black text with a black border. The following image demonstrates how the Yes, Please button appears in the browser when focused:

      Two buttons below a paragraph of text. One button is focused with black text and black border with a yellow background. The other button is light blue with dark blue text.

      In this section, you used the :focus pseudo-class to create custom styles that appear when the website visitor uses their keyboard to navigate the page. In the next section, you will use the :active pseudo-class to indicate when a user is interacting with an element via a mouse click or a keypress.

      Applying the :active Pseudo-Class

      The next pseudo-class that you will work with is the :active state of an interactive element. The active state is the state at which an element is interacted with, typically via a mouse down or mouse click action. This provides the opportunity to give the visitor a clear state to indicate a successful mouse click and button press.

      To begin working with the :active pseudo-class, open styles.css in your text editor. Following the group selector block for a:focus, .link:focus, add a new selector block with the group selector a:active, .link:active. Give color a value of #808, which will create a darker pink than the :hover state.

      Note that some browsers will mix the styles of a :focus pseudo-class and an :active pseudo-class. To prevent this, you will need to remove the outline and background-color properties by setting them to none and transparent, respectively:

      styles.css

      ...
      a:focus,
      .link:focus {
        ...
      }
      
      a:active,
      .link:active {
        color: #808;
        outline: none;
        background-color: transparent;
      }
      ...
      

      Save the addition of the :active pseudo-class to your styles.css file, then reload index.html in your web browser. The following animation shows how the :active state changes from the pink to darker pink as the mouse is clicked while over the this tutorial link.

      An animation involving a paragraph of text with a link with a hand cursor icon over the link. The link text is underlined and is alternating between a dark pink and a darker pink.

      Next, to apply an active state to .button, return to styles.css in your text editor. Add a .button:active pseudo-class selector and apply styles that are dark variants of the :hover styles. For the color property, set the value to a light gray with #ddd. For both the background-color and border-color properties, set the value to a dark blue with a value of #127. The highlighted sections of the following code block demonstrate how this is written:

      styles.css

      ...
      .button:focus {
        ...
      }
      
      .button:active {
        color: #ddd;
        background-color: #127;
        border-color: #127;
      }
      

      Be sure to save these changes to styles.css, then return to your browser to refresh index.html. Hover your mouse cursor over one of the two buttons in the bottom of the content and then click down. The button will change from a light blue background with blue text and border to a full blue button with white text when hovered, then to a dark blue button with light gray text when clicked. The following animation demonstrates how this change from the :hover to the :active state appears as the mouse button is clicked:

      An animation of cursor pressing a button turning the button from blue with white text to a dark blue with light gray text.

      You created a visual indicator of a mouse button press event by using the :active pseudo-class to change the styles when that event occurs. In the next section, you will use the :visited pseudo-class to provide an indicator of which <a> elements with a href attribute have visited that link.

      Applying the :visited Pseudo-Class

      The :visited pseudo-class is unlike the previous pseudo-classes covered in this tutorial. Where the previous pseudo-classes involve an active interaction of the user with the element, the :visited pseudo-class indicates that an element was previously interacted with. Specifically, the :visited pseudo-class indicates which <a> with an href attribute are present in the browser history, meaning those links have been visited.

      To create a customized :visited indicator on the text links, open your styles.css file in your text editor. Below the a:active, .link:active group selector, add a new group selector targeting a a:visited, .link:visted group selector. The default :visited link style is commonly a purple color. For the purposes of the demo, the :visited color will be a dark green.

      Add a color property with a value of #080, as shown in the following highlighted code:

      styles.css

      ...
      a:active,
      .link:active {
        ...
      }
      
      a:visited,
      .link:visited {
        color: #080;
      }
      ...
      

      Save this change to the styles.css file and then open index.html in your web browser. If you haven’t, go ahead and click the this tutorial and No, Thank you <a> element links. Both of these links will have a text color of dark green, as shown in the following image:

      Pragraph of text with a visited link underlined and green and two buttons below. One of the button’s text is green instead of dark blue.

      Now, the green text in the button distracts from the purpose of the No, thank you button. Additionally, when the visited links are interacted with again with a :hover or :active state, the dark green remains instead of the defined colors for each respective state.

      To address these scenarios, return to your styles.css file in your text editor. First, append the a:hover, .link:hover group selector with the added scenario of a :visited element that has an active :hover state by adding a:visited:hover, .link:visited:hover. Likewise, expand the a:active, .link:active selector block with a:visited:active, .link:visited:active. Lastly, the desired visited state for the .button element is to be styled the same as the default state. As such, the .button selector needs to become a group selector of .button, .button:visited, so the visited state appears the same as the default state:

      styles.css

      ...
      a:hover,
      .link:hover,
      a:visited:hover,
      .link:visited:hover {
        color: #b1b;
      }
      ...
      a:active,
      .link:active,
      a:visited:active,
      .link:visited:active {
        color: #808;
      }
      
      a:visited,
      .link:visited {
        color: #080;
      }
      ...
      .button,
      .button:visited {
        ...
      }
      
      .button:hover,
      .button:visited:hover {
        color: white;
        background-color: #25a;
        border-color: #25a;
      }
      ...
      

      Save your changes to the styles.css file and reload index.html in the web browser. The text default :visited link now appears in the desired dark green color, while the button-style link retains the button look. The following image demonstrates how this will appear in the browser.

      Paragraph of text with a visited link underlined and green and two similarly-styled buttons below.

      You used the :visited pseudo-class to create styles specific to when a link is present in the browser history, indicating to the user links that have been visited. This section concludes the work on pseudo-classes and using them to define specific styles for a given state. In the final section of this tutorial, you will use the transition property to create a seamless animation between these different pseudo-class states.

      Using transition to Animate Between States

      When working with states of elements, a shift in styles between the states can be abrupt. The transition property is used to blend and animate the styles from one state to the next state to avoid this abruptness. The transition property is a shorthand property that combines the transition-property, transition-duration, and transition-timing-function properties.

      The transition-property can be any property that has a value calculated between two given values. Colors are included in this, as they are numerical values, even when a color name is used. The transition-duration property is a numeric value for how long the transition should occur. The value for the duration is often represented in seconds, with the s unit, or milliseconds, with the ms unit. Lastly, the transition-timing-function controls how the animation will play out over time, enabling you to make subtle changes to enhance the animation.

      To begin working with the transition property, open your styles.css file and go to the a, .link group selector and the .button, .button:visited group selector. Add a transition property with a value of all 0.5s linear. The all is the value for the transition-property, which tells the browser to animate all the properties that change between the states. The 0.5s is the transition-duration value and equates half a second; this can also be represented as 500ms. Lastly, the linear position is the transition-timing-function value, which tells the browser to move from one value to the next in a constant increment throughout the duration:

      styles.css

      ...
      a,
      .link {
        ...
        transition: all 0.5s linear;
      }
      ...
      .button,
      .button:visited {
        ...
        transition: all 0.5s linear;
      }
      

      Save your changes to styles.css and then open index.html in your web browser. Once the page loads, begin interacting with the link and button elements and pay attention to how the styles animate between the different states. The following animation shows the button style transitioning from the default state to the :hover pseudo-class state:

      Animation of a cursor hovering a button and the button transitions styles from a light blue button with blue text to a blue button with white text.

      To make the animations feel more snappy and natural, return to your styles.css file and adjust the transition property values. For the a,.link group selector, change the duration from 0.5s to 250ms, which is half the duration compared to what it was before. Then change the linear timing function value to ease-in-out. This will create an animation that starts off slow, speeds up in the middle, and slows down to the end. Then, for the .button,.button:visited group selector, change the duration to a quicker 180ms and set the timing function to the same ease-in-out value as the link:

      styles.css

      
      ...
      a,
      .link {
        ...
        transition: all 250ms ease-in-out;
      }
      ...
      .button,
      .button:visited {
        ...
        transition: all 180ms ease-in-out;
      }
      

      Save these changes to your styles.css file and then refresh the index.html page in your web browser. The transition animations between states will still animate, but are now much quicker and feel faster, too. With the transition property, it is important to play around with the values to find an animation that fits the design. The following animation demonstrates the faster transition of the button from the default state to the :hover state to the :active state.

      Animation of a cursor hovering a button and the button transitions styles from a light blue button with blue text to a blue button with white text. Then the cursor moves and hovers a green underlined link and link fades to a pink color.

      You have now created an animation between states. The transition property helps make changes between states more engaging and fun.

      Conclusion

      Providing clear differences between interactive element states is a valuable asset to a website. States help communicate important concepts to the website’s visitor by providing visual feedback to an interaction.

      In this tutorial, you have successfully used the primary state pseudo-classes to create multiple styles for different interactive elements. You also learned that there are different purposes behind the <button> and <a> elements, but visually they can communicate similar concepts. Lastly, you used the transition property to provide smooth animations between these states to create more engaging interactive elements. It is important to keep these four states in mind when creating a website so the visitor is given this important interactive feedback.

      If you would like to read more CSS tutorials, try out the other tutorials in the How To Style HTML with CSS series.



      Source link