Files
savvy-firebase/firebase.md
2020-04-12 03:07:19 +01:00

33 KiB

Firebase

API documentation (JS): https://firebase.google.com/docs/reference/js Guides documentation: https://firebase.google.com/docs/guides

Handling errors

List of errors: https://firebase.google.com/docs/reference/js/firebase.auth.Auth#createuserwithemailandpassword

You can catch the error and use if/else to capture the specific error.

firebase
  .auth()
  .createUserWithEmailAndPassword(email, password)
  .catch(function(error) {
    // Handle Errors here.
    var errorCode = error.code;
    var errorMessage = error.message;
    if (errorCode == "auth/weak-password") {
      alert("The password is too weak.");
    } else {
      alert(errorMessage);
    }
    console.log(error);
  });

Firebase User object: https://firebase.google.com/docs/reference/js/firebase.User

All properties that it takes/available listed above

Create new User

Use firebase.auth().createUserWithEmailAndPassword(email, password) to create a new user.

Only an email address and password is needed to create a new account. The user itself has a few additional attributes you can attach to it - the displayName and the photoURL among a few others. You can see all of them in the properties of the user object: https://firebase.google.com/docs/reference/js/firebase.User#properties

Any additional information you want to store alongside the user should use a firestore. You should have a collecton for users, then each document should be named after the User.uid and contain the additional data you want to store.

Update password/email

The instructions to update a password/email: https://firebase.google.com/docs/auth/web/manage-users#re-authenticate_a_user mention using reauthenticateWithCredential. This is only needed if using a 3rd party auth method which returns this credential object. If using native firebase logins then simply forcing the user to resign in is sufficient.

Adding material icons to Vuetify project

yarn add @mdi/font -D

Controlling layouts

Spacing

https://vuetifyjs.com/en/styles/spacing/

You can use helper classes to apply custom margins/padding to elements.

You can apply padding to left: class="pl-3". Or apply margin to all: class="ma-4".

You can use this to center objects without using flex: class="mx-auto".

Display helpers

The display helpers allow you to control the dispaly of content. These can be used to hide/show elements based on the current viewport, or the actual display element type.

https://vuetifyjs.com/en/styles/display/

Hide/Display elements for different widths

You can hide specific viewport sizes with: https://vuetifyjs.com/en/styles/display/#visibility

d-flex by default without any size will apply to xs and above.

You can make things visible only on one viewport with: class="d-none d-lg-flex d-xl-none"

You can make things hide only on one viewport with: class="d-lg-none d-xl-flex"

You can hide multiple by using combinations of the above, or, use the following lateral display helper classes: class="hidden-md-and-up" and class="hidden-sm-and-down"

flex

Using flex will make the object fill the entire viewport, rather than adhering to the margins/widths by default. There is a lot more to flex - you can make things align left/right, vertical alignment and more: https://vuetifyjs.com/en/styles/flex/#flex

cols

In a <v-row> you can have many <v-col>. You can have as many of these and use the cols prop to control how wide the content should be.

<v-col cols="4" class="appTitle d-flex justify-start">
  <v-toolbar-title>
    Savvy Firebase tutorial
  </v-toolbar-title>
</v-col>

You can combine this with d-flex and justify to control the positioning and alignment:

https://vuetifyjs.com/en/styles/flex/#flex-justify

Justify classes include:

  • justify-start
  • justify-end
  • justify-center
  • justify-space-between
  • justify-space-around

Using cols, d-flex and justify you can control precisely how things should be laid out for different screen sizes.

  • Use <v-col>, use the prop cols="" to control how wide it should be.
  • Use <v-spacer>, to dynamically fill space.
  • Use class="d-flex" and the justify classes.
  • Use class="d-none d-xl-flex" to control layouts for different sizes.
  • Use class="hidden-md-and-up" to quickly control layouts for different sizes.

Dual layouts

You can have the same html element for different sizes.

The layout you want for the screen you want to break on should not include it.

For example, to break on the medium viewport and have the second layout apply to it:

<v-app-bar flat color="indigo" app class="hidden-md-and-up"></v-app-bar>
<v-app-bar flat color="indigo" app class="hidden-sm-and-down"></v-app-bar>

Here the second layout does not include the medium layout, hence the medium layout will apply to it.

App bar

Gradient as a background colour

The background image to the app bar is provided with a src prop.

If you want to apply a fade gradient colour on top of the image, you should use a scoped slot:

<v-app-bar
  flat
  app
  class="hidden-sm-and-down"
  src="https://www.stellamccartney.com/cloud/smcwp/uploads/2016/01/1920x1080-black-solid-color-background.jpg"
>
  <template v-slot:img="{props}">
    <v-img
      v-bind="props"
      gradient="to top right, rgba(100,115,201,.7), rgba(25,32,72,.7)"
    ></v-img> </template
></v-app-bar>

If you want to just use a gradient (no img), then apply a solid colour image and then use a scoped slot with the gradient you want.

Router

When linking to routes, you should use <router-link>:

<router-link :to="{ name: 'Login' }">
  <span class="mr-3">Sign In</span>
</router-link>

You can apply transitions on a route change:

<transition name="slide">
  <router-view></router-view>
</transition>

vue-responsive-video-background-player

https://github.com/avidofood/vue-responsive-video-background-player

yarn add vue-responsive-video-background-player

ffmpeg -an -i Optical\ Fibers\ 1.mov -vcodec libx264 -pix_fmt yuv420p -profile:v baseline -level 3 optical.mp4

https://cli.vuejs.org/guide/html-and-static-assets.html#disable-index-generation

If using the public folder (not ./assets) then follow these instructions: https://cli.vuejs.org/guide/html-and-static-assets.html#the-public-folder

For videos in ./assets do the following:

export default {
  data() {
    return {
      video: require("../assets/videos/optical.mp4")
    };
  }
};

and reference it in the component:

<video-background :src="video" style=" height: 100vh;"> </video-background>

This is done this way because webpack will apply custom names to the assets - this is so it can handle caching.

You can find full props to customise the video: https://github.com/avidofood/vue-responsive-video-background-player#props

You can set a gradient to the image to improve visibility:

style = " height: 100vh;";
overlay = "linear-gradient(45deg,#2a4ae430,#fb949e6b)";

Dynamically hide app bar

Use the Vuex store to determine when you want to hide the app bar:

  state: {
    fullScreen: false
  },
  getters: {
    fullScreen: state => {
      return state.fullScreen
    }
  },
  mutations: {
    fullScreen(state, fullScreen) {
      state.fullScreen = fullScreen;
    }
  },

and create the lifecycle actions in the component that houses the appbar:

  created () {
    this.$store.commit("fullScreen", true);
  },
  beforeDestroy () {
    this.$store.commit("fullScreen", false);
  },

Then you can wrap the appbar in a v-if:

<v-app-bar
  flat
  app
  class="hidden-sm-and-down"
  color="#EEEEEE"
  v-if="!this.$store.getters.fullScreen"
></v-app-bar>

Change default font colour

In _variables.scss create a variable with the colour you want:

$mainColor: #323947;

Then create a new class fontColor to apply to the router, and edit the default theme--light class for Vuetify to apply this colour:

.theme--light.v-application,
.fontColor,
.theme--light.v-sheet {
  color: $mainColor !important;
  // color: red !important;
}

Then in App.vue apply the fontColor class to the <v-app>:

<v-app
  :style="{ background: $vuetify.theme.themes.light.background }"
  class="fontColor"
></v-app>

Grids

https://vuetifyjs.com/en/components/grids/#row-and-column-breakpoints

In addition to using the flex classes you can use the grid system. The grid system allows you to create rows and columns and use props to control how the content should be displayed.

This playground demonstrates what align and justify can do: https://vuetifyjs.com/en/components/grids/#playground

The grid system applies to <v-row> and you can use the following props:

  • align controls the y-axis. You have start, center, end, baseline and stretch.
  • justify controls the x-axis. You have start, center, end, space-around and space-between.

For <v-col> only align-self is available. To align content on a column, or any other element like a card, you should use the same classes as in the flex system. For example:

<v-col cols="5" align-self="start" class="d-flex justify-end pr-0"></v-col>

Container filling whole page

A container should be used whenever you want to use <v-col> and <v-row>. To make this container fill the whole page (and be able to use align props and flex classes) you should specify the following two props:

  • fill-height
  • fluid.

To make content fill the width of the element it is in, you can use the following css:

.full-width {
  width: 100%;
}

width sets the element to the percentage of its parent. So to have a textbox fill the width of the v-card element it is in you should set the <v-card>, <v-form> and <v-text-field> to width: 100%;. As long as the parent elements have 100%, you can set the text input to be a percentage of this, say 50%.

Multi column layouts

An example can be found here: https://git.panaetius.co.uk/web-development/savvy-firebase/src/branch/base-template/src/views/forms/LoginForm.vue

You can make use of the above to create dynamic layouts.

One idea (for a login page) might be to use the following nested layout:

Container

A <v-container> with fill-height and fluid props.

Row

A <v-row>

Columns

A <v-col> with align-self="stretch" and class="d-flex flex-column justify-space-between".

align-self=stretch allows each child of the column to stretch (https://developer.mozilla.org/en-US/docs/Web/CSS/align-items).

class="d-flex flex-column justify-space-between" allows each child of the column to flex - with justify-space-between putting them equally apart top to bottom. It's used to equally space out the rows inside this column. Even though we are not using <v-row>.

Cards

A <v-card> with class="d-flex align-end flex-column".

class="d-flex align-end flex-column" allows content inside the card to flex. The align-end means they will be on the right hand side and flex-column means they are flexing across columns.

To make text align right, you should use the text-right class:

<p class="whiteText font-regular text-right mb-0">

This is because although the <p> element will align to the right becasue of the class from the <v-card>, the text inside will align to the left.

We can add color="rgb(0, 0, 0, 0)" and flat props to the card - allowing it to seamlessly blend in with the background.

We can add mulitple cards to this layout in this column - and control how they are spaced with the props on the column.

We can add <v-spacer> and then add additional colums with the same props to add content in a column like manner.

Important!

If you want content to be aligned in a column with each item on a new line, then use:

<v-col cols="6" align-self="center" class="">
  <v-card color="rgb(0, 0, 0, 0)" flat class="d-flex align-center flex-column">
    <v-icon color="white" size="3em">mdi-account-circle</v-icon>
    <p class="mb-0 display-2">Login</p></v-card
  ></v-col
>

Helper classes

Spacing

https://vuetifyjs.com/en/styles/spacing/

You can use classes like ma-2 and pd-3 to control margin/padding in directions for any component.

See the playground for a quick demonstration on what each one does:

https://vuetifyjs.com/en/styles/spacing/#playground

Typography

There are quite a few helper classes available to control font sizes and styles.

https://vuetifyjs.com/en/styles/typography/

There are two types of styles: Typography Display Classes and Style and Weight Classes.

Examples include .display-2 and .font-weight-black.

Replace Vuetify default font with custom

Fonts needed to replace the Roboto default:

  • Thin
  • Regular
  • Medium
  • Light
  • Condensed Light (Light)
  • Bold
  • Black
  • italic
  • Light italic
  • Medium italic
  • Bold italic

If font styles dont exist then substitute them with the closest type.

An example of a stylesheet replacing all of these with the Gilroy font is here: https://git.panaetius.co.uk/web-development/savvy-firebase/src/branch/base-template/src/scss/_variables.scss

Custom colour theme

Creating a custom colour theme for Vuetify is straightforward.

https://mycolor.space is a good resource to generate palettes from a colour.

See https://vuetifyjs.com/en/customization/theme/#customizing for details on customising a theme.

Editable colours

You change the Vuetify object in vuetify.js:

export default new Vuetify({
  icons: {
    iconfont: "mdi"
  },
  theme: {
    themes: {
      light: {
        primary: "#051937",
        secondary: "#374366",
        accent: "#d4a418",
        background: "#e8e8e8"
      }
    }
  }
});

These are the editable colours you can choose for Vuetify

{
  "primary": "#1976D2",
  "secondary": "#424242",
  "accent": "#82B1FF",
  "error": "#FF5252",
  "info": "#2196F3",
  "success": "#4CAF50",
  "warning": "#FFC107"
}

These follow the Material style of colours: https://material.io/design/color/the-color-system.html#color-theme-creation

The accent colour in Vuetify isn't really specified in the Material colour design - typically the secondary colour would be used as an accent colour - with shades of the primary used throughout the UI. However in some themes you may wish to use primary and secondary together, and have a seperate accent colour.

https://material.io/design/color/the-color-system.html#color-theme-creation

Colour variants

Vuetify will automatically generate darken and lighten colour variants of the primary and secondary colours in the theme. If you want to control these manually you can do so following the instructions: https://vuetifyjs.com/en/customization/theme/#custom-theme-variants

The variants you can use are:

{
  "base": "string",
  "lighten5": "string",
  "lighten4": "string",
  "lighten3": "string",
  "lighten2": "string",
  "lighten1": "string",
  "darken1": "string",
  "darken2": "string",
  "darken3": "string",
  "darken4": "string"
}

You can apply these as a class to the object you want to darken/lighten. For example: <v-btn color="primary" class="darken-2">Hello</v-btn>

Forms

Changing the colour of form inputs

You can change the background colour, and the highlight colour (when clicked) with the following props:

<v-text-field
  v-model="username"
  background-color="rgb(100%, 100%, 100%, 10%)"
  color="rgb(100%, 100%, 100%, 10%)"
  class="white-placeholder"
></v-text-field>

To change the placeholder (text) colour you should use a custom class that overwrites this colour:

.white-placeholder ::v-deep input::placeholder {
  color: white !important;
  opacity: 1;
}

To change the label (text) colour:

.white-placeholder ::v-deep .v-label {
  color: white !important;
  opacity: 1;
}

To change the input colour (the text that is typed in by the user) we should use another custom class:

.white-placeholder ::v-deep input {
  color: white !important;
  opacity: 1;
}

We have to use ::v-deep because we are in a scoped component. Any nested css classes/variables in a scoped component will have a random id attached to them (vue does this dynamically). The ::v-deep will make sure that the nested css gets the formatting applied. See https://vue-loader.vuejs.org/guide/scoped-css.html#deep-selectors for details.

Overwriting sass/scss variables

https://vuetifyjs.com/en/customization/sass-variables/

Create a _variables.scss in ./src/scss.

If Vuetify is installed through the Vuetify CLI then this scss file will be automatically bootstrapped into the App. If you want to write additional sheets then you should edit the vue.config.js file and add the following to module.exports:

module.exports = {
  css: {
    loaderOptions: {
      scss: {
        prependData: `
          @import "@/scss/_variables.scss";
        `
      }
    }
  },
  transpileDependencies: ["vuetify"]
};

Overwriting sass variables is easy. You need to simply define them in this _variables.scss file and they will be used in the main app.

You can find a list of default variables https://vuetifyjs.com/en/customization/sass-variables/.

Or you can use the variables defined in the sass section of the components api documentation.

Slots

When using slots in Vuetify find the slot name from the api documentation.

To use a slot, you should create a <template> inside the component and pass a v-slot:$NAME directive.

For example: <template v-slot:prepend-inner>

You can also use # as a keybinding for v-slot: <template #prepend-inner>

An example of passing a prepend icon to a <v-text-field>:

<template #prepend-inner>
  <v-icon>
    mdi-account-circle
  </v-icon>
</template>

Expects the v-img component. Scoped props should be applied with v-bind="props".

Forms

Forms should be done dynamically - creating a v-for on a text field input. You should create all the props values in an object in data.

The <v-form> should specify a v-model prop, which should reference a boolean on whether the form is valid or not. You should set a ref prop, which is the name you will refer to the form.

<v-form
  v-model="valid"
  ref="form"
  class="pt-4 d-flex flex-column align-center full-width"
></v-form>

You would refer to this form with this.$refs.form.

<v-text-field
  v-for="field in formFields"
  :key="field.name"
  outlined
  rounded
  required
  :type="field.password ? 'password' : 'text'"
  background-color="rgb(100%, 100%, 100%, 10%)"
  color="rgb(100%, 100%, 100%, 20%)"
  class="white-placeholder full-width"
  v-model="field.value"
  :rules="field.rules"
  :placeholder="field.placeholder"
  :success="!!field.value"
  @click:append="field.showIconData = !field.showIconData"
>
  <template #prepend-inner>
    <v-icon color="white" class="pr-3">
      {{ field.prependIcon }}
    </v-icon>
  </template>
  <template #append>
    <div class="innerIcon">
      <v-btn
        v-if="field.appendIconShow"
        flat
        icon
        text
        x-small
        @click="
          () => {
            field.showIconData = !field.showIconData;
            field.password = !field.password;
          }
        "
      >
        <v-icon color="white">
          {{ field.showIconData ? field.appendIconShow : field.appendIconHide }}
        </v-icon>
      </v-btn>
    </div>
  </template>
</v-text-field>

Here we have created the text fields we want to render and we are looping over formFields to fill in the props and slots.

{
valid: false,
load: false,
formFields: [
  {
    name: "Email",
    rules: [
      v => !!v || "You must enter an email address.",
      v => /.+@.+/.test(v) || "Email is not valid."
    ],
    placeholder: "Email",
    successmessage: "Email is valid.",
    prependIcon: "mdi-at",
    value: ""
  },
  {
    name: "Password",
    rules: [
      v => !!v || "You must enter a password.",
      v => (v && v.length >= 8) || "Minimum 8 characters."
    ],
    placeholder: "Password",
    successmessage: "Password is valid.",
    prependIcon: "mdi-lock",
    appendIconShow: "mdi-eye",
    appendIconHide: "mdi-eye-off",
    showIconData: false,
    value: "",
    password: true
  }
]
}

This is the data object in the <script>. Here we define everything we need for the form, and specify all props needed for the form to work.

Validation

Validating a form is done on the inputs that make up the form with the rules prop. This prop takes an array where each one of them must resolve to true in order for the form to be valid. We use arrow functions as the value of the input field is passed into the rules in order to check whether or not it is valud.

Submitting and check

You should create a <v-btn> component and a method that will be called with @click.

<v-btn
  depressed
  large
  color="primary"
  class="lighten-3"
  :loading="load"
  @click="submit"
  >Submit</v-btn
>
async submit() {
  this.load = !this.load;
  console.log("loading");
  if (this.$refs.form.validate()) {
    console.log("Success");
  } else {
    console.log("Not valid");
    this.load = !this.load;
  }
}

Here we are checking the validity of the form, and enabling/disabling the loading prop based on whether or not the form is correct.

Hints

The following is a codepen showing how you can dynamically populate a hint to show if the form is submitted with empty values. This can be useful if don't want to show an error message, but you still want the form to provide feedback on what to fill in.

https://codepen.io/anon/pen/GeVQLG

Triangle backgrounds

Designs

Dual image: https://codepen.io/eddyerburgh/pen/EPYVVX background-image: linear-gradient( 109.6deg, rgba(113,14,51,0.83) 15.2%, rgba(217,43,23,0.95) 96.8% );

Animations

Vue Animations: https://css-tricks.com/intro-to-vue-5-animations/

transitions

You can use Animista css animations with Vue's transition element: https://serversideup.net/animista-css-with-vuejs-transitions/

Router Animations

https://github.com/Orlandster/vue-page-transition

yarn add vue-page-transition

import VuePageTransition from "vue-page-transition";
Vue.use(VuePageTransition);

You should wrap the <router-view>:

<vue-page-transition name="fade-in-right">
  <router-view />
</vue-page-transition>

A list of all transitions is here: https://orlandster.github.io/vue-page-transition/#/

Animate on scroll

You can use vue transitions or you can use a helper library.

https://michalsnik.github.io/aos/ can be used to quickly apply animations on scroll.

To use install with yarn: yarn add aos@next

Then, in main.js add:

import AOS from "aos";
import "aos/dist/aos.css";

And then add to the Vue instance the AOS.init:

new Vue({
  created() {
    AOS.init();
  },
  router,
  store,
  vuetify,
  render: h => h(App)
}).$mount("#app");

You can then in any component add any of the animations from the documentation: data-aos="zoom-in"

To use with nuxt install as a plugin: https://www.yasminzy.com/nuxt/aos.html#steps

Custom animations with AOS library

Custom Cubic-bezier easings: https://cubic-bezier.com Custom easings: https://easings.net/en

You can use custom animations with the AOS library. For example, we can use Animista.

The base template is:

[data-aos="test-roll"] {
  // initial here
  transition-property: transform, opacity, filter;
  &.aos-animate {
    // final here
  }
}

In Animista you should copy the keyframes. Take the 0% content and put it as the intitial content in the above template.

The the 100% content and place it in the final content in the above template.

You need to update the transition-property with the elements you are animating from one state to another.

Using custom easings is straightforward.

Create the scss:

[data-aos] {
  body[data-aos-easing="new-easing"] &,
  &[data-aos][data-aos-easing="new-easing"] {
    transition-timing-function: cubic-bezier(0.25, 1, 0.57, 0.55);
  }
}

Then apply in the html:

<v-card
  flat
  color="rgb(0, 0, 0, 0)"
  class="d-flex align-center justify-center flex-column pa-6"
  data-aos="test-roll"
  data-aos-duration="3000"
  data-aos-easing="new-easing"
></v-card>

Alternative to fill-height

Using fill-height prop on a <v-container> can result in a bug where the width of the container doesn't reach all the way to the right hand side.

An alternative is to use d-flex on the <v-container>:

<v-container
  fluid
  class="d-flex align-center"
  style="height: 100vh;"
></v-container>

document usign align with viewport set a default which applies to all, set the one you want to apply for upwards

document setting a default cols (check this with triangle)

Dynamic Mobile Layouts

Cols

https://git.panaetius.co.uk/web-development/savvy-firebase/src/branch/base-template/src/views/forms/LoginForm.vue

You can control how items are rendered based on the viewport width. For example, you can have a page appear across 2 columns on desktop, but display a single column on mobile : http://localhost:8080/triangle

One way is to use the the viewport cols props for <v-col>:

<v-col
  sm="6"
  cols="12"
  align-self="stretch"
  class="d-flex justify-sm-end justify-center pb-10 pb-sm-0 pr-sm-10"
></v-col>

This will set a default of 12, and then use 6 on sm and anything above it.

You can find all props for the dynamic cols in https://vuetifyjs.com/en/components/grids/#api

If you set a row to use more than 12 columns, it will overfill on to the next line.

Using the cards to create dynamic rows filling the space in each column, you can go even further and control how this should render on different viewports.

For example, if we wanted to center content on a mobile, but have it align to the right on larger screens we can do the following:

<div
  class="d-flex flex-column align-sm-end align-center justify-center"
  :class="alignOnViewport"
>

Here we are using align-center. This will set the content to center itself by default. We can then use align-sm-end to align the content to the right on anything sm and above. Combinations of these can be used to create dynamic and fluid layouts for all devices.

You can also use dynamically created classes to apply css to the content depending on the viewport.

CSS Spacing

https://vuetifyjs.com/en/styles/spacing/

You can use the helper classes pa-10 to control padding/margins.

You can also viewport widths in these too: pb-xs-10 pr-sm-10

When wanting to use a default value and only apply another one on a certain viewport:

class="d-flex justify-sm-end justify-center pb-10 pb-sm-0 pr-sm-10"

This will set a padding on the bottom of 10 for xs and then 0 for anything higher.

Dynamically Apply Classes

https://michaelnthiessen.com/dynamically-add-class-name/

Apply classes based on viewport

You can use the breakpoint variables: https://vuetifyjs.com/en/customization/breakpoints/#breakpoint-service-object

In javascript to determine what the breakpoint/size/dimension etc is. You should prepend: $vuetify.breakpoint before each breakpoint service object.

You can also use a neat javascript check with switch and case to create a computed variable:

computed: {
  imageHeight () {
    switch (this.$vuetify.breakpoint.name) {
      case 'xs': return '220px'
      case 'sm': return '400px'
      case 'md': return '500px'
      case 'lg': return '600px'
      case 'xl': return '800px'
    }
  },
}

Alternatively, you can create a normal computed variable and reference it in your props:

computed: {
  isXSmall() {
    return this.$vuetify.breakpoint.xsOnly;
  }
}

You can then dynamically set classes with the :class prop:

<div
  class="d-flex flex-column align-sm-end align-center justify-center"
  :class="[isXSmall ? 'text-align-center' : 'text-align-right']"
></div>

You can use both class and the prop :class together. You can use different methods to do this, here we used an array, but you can also use a javascript object. https://michaelnthiessen.com/dynamically-add-class-name/

A cleaner way to do this is to use computed properties exclusively, either returning a single value, or an array as above:

computed: {
  alignOnViewport() {
    return this.$vuetify.breakpoint.xsOnly
      ? "text-align-center"
      : "text-align-right";
  }
}

Using the :class prop:

<div
  class="d-flex flex-column align-sm-end align-center justify-center"
  :class="alignOnViewport"
></div>

SVG

Viewbox

https://css-tricks.com/scale-svg/

Viewbox is an attribute on the <svg> element and has 4 components: x, y, height, width.

The x and y set the coordinate system to use for the top left corner of the viewport. Simple scaling can be done by setting these to 0,0, but you could set them to something else (like central) to make drawing circles using radius easier, or for easier transforming/rotating.

Take for example viewBox="-50 -50 100 100" - this means the top left corner has coordinates -50,-50, so the bottom right corner has coordinates 50,50. Drawing a circle centered around 0,0 with radius 50 it will fill the screen.

Setting a viewbox allows you to set the box that the svg element should draw in. This will allow you to then control the width and height to scale the element properly.

Triangle banners

Method 1 - position: absolute

See https://git.panaetius.co.uk/web-development/savvy-firebase/src/branch/base-template/src/views/Triangle.vue for an example.

You should create a triangle banner css class:

.newtriangle {
  width: 105vw;
  height: 50vh;
  background-image: radial-gradient(
    circle farthest-corner at 7.5% 54.1%,
    rgba(0, 0, 0, 1) 0%,
    rgba(39, 0, 89, 1) 74.9%
  );
  -webkit-clip-path: polygon(0 0, 100% 35%, 100% 65%, 0% 100%);
  clip-path: polygon(0 0, 100% 35%, 100% 65%, 0% 100%);
}

Then you should create a <v-row> with this class. This row should also set position: absolute; z-index: 1; in the style (or add this to the triangle class).

<v-row class="d-flex newtriangle" style="position: absolute; z-index: 1;">
  <v-col
    class="d-flex align-center pt-0 ml-8"
    style="position: relative; z-index: 10;"
  >
    <v-card flat color="rgb(0, 0, 0, 0)">
      <h1 class="display" style="position: relative;">
        Welcome to Savvy Firebox Tutorial
      </h1>
      <v-btn
        x-large
        color="primary"
        style="position: relative; z-index: 10;"
        class="lighten-1"
        >Click Here</v-btn
      >
    </v-card>
  </v-col>
</v-row>

Because the element has been set to position absolute, any content after this without absolute positioning will be on top of this element. You should create a blank row with position: relative; with a height matching the triangle banner above it. This will create blank space on top of the banner.

<v-row class="d-flex" style="position: relative; height: 50vh;"> </v-row>

Any other content then be placed after this like normal.

Method 2 - Without position: absolute

See https://git.panaetius.co.uk/web-development/savvy-firebase/src/branch/base-template/src/views/Backgrounds.vue for an example.

Add the css class for the banner:

.newtriangle {
  width: 105vw;
  height: 50vh;
  background-image: radial-gradient(
    circle farthest-corner at 7.5% 54.1%,
    rgba(0, 0, 0, 1) 0%,
    rgba(39, 0, 89, 1) 74.9%
  );
  -webkit-clip-path: polygon(0 0, 100% 35%, 100% 65%, 0% 100%);
  clip-path: polygon(0 0, 100% 35%, 100% 65%, 0% 100%);
}

Apply this class to a <v-content> and add any content to go inside it:

<v-container fluid class="newtriangle">
  <v-row class="fill-height d-flex align-center">
    <v-col class="">
      <h1 class="display-2 white--text">
        CSS polygon background with clip-path.
      </h1>
    </v-col>
  </v-row>
</v-container>

Fullscreen backgrounds

You can use svg images as a fullsize background. These images can be obtained from envano web templates - use Pixelmator Pro and its extraction tools to extract the svg elements, upscale and export for web.

Create a <v-img> with the src as the svg image.

<v-img
  src="../assets/images/background.svg"
  :height="this.currentHeight - 64"
  width="auto"
  contain="false"
  position="bottom 0px right 0px"
></v-img>

Any content to go in the page should go inside the <v-img> tags.

The width should be auto - it will be set by the height.

The prop contain should be used to ensure the image fits the full height properly and isn't cropped.

To align the image in the bottom right, set the position prop:

position="bottom 0px right 0px"

If you are using a appbar, you should dynamically get the viewport height, and substract the height of the appbar. The default height is 64px - use the documentation to find the heights if using dense or extended props.

You should add a computed property that returns the current viewport height:

computed: {
  currentHeight() {
    return window.innerHeight
  }
}

Then, in the height of the <v-img> you should set a dynamic prop that sets the height of the image to this computed property minus the height of the appbar.

:height="this.currentHeight - 64"