solution • Vuetify templates - martindubenet/wed-dev-design GitHub Wiki
[ Vue.js coding ][ Vuetify templates ][ Vue.js setup for Vuetify ][ Vue headless CMS site ]
-
Features guide
- Sass.scss variables
-
Programmatic scrolling via
goTo()
.
- Components
-
Application to help create the main
<v-app.vue
file that will contain the<v-main>
and probably the<navigation-drawer>
.
-
Application to help create the main
When having two interactions set on a single button declare them as an array within the v-on
parameter.
In this example, a mouse-hover activates a
V-Tooltip
while the click activates theV-Dialog
.<v-dialog v-model="modalDialog"> <template #activator="{ on: dialog, attrs }"> <v-tooltip top> <template v-slot:activator="{ on: tooltip }"> <v-btn v-bind="attrs" v-on="{ ...tooltip, ...dialog }" @click="redirecToPage2" … /> </template> <p>Tooltip text lorem ipsum</p> </v-tooltip> </template> … </v-dialog>
Scoping the stylesheets adds specificity to your CSS based on the HTML data attributes (<div data-v-…>
) dynamically generated by Vue.js on the « component.vue » rendered in the DOM.
<style lang="scss" scoped> .selector{} </style>.selector[data-v-…] {}
This is a deep selector that allows to target styles from within a parent or grand-parent to a child component. We could resume the logic of ::v-deep
to some kind of CSS !important
declaration where they both kind of bypass the cascading logic of CSS.
::v-deep .child-component-selector .example { … }
.parent-selector::v-deep .child-component-selector .example { … }
This is how Vuetify propose in their doc about Sass Material Color Pack : Set the Sass variable $color-pack
to false
so we don't import all Vuetify Material Colors Sass files in order to customise some parameters for our solution BEFORE compiling it.
// src/sass/yourFile.scss
// Parameter
$color-pack: false;
// Load Vuetify within your Sass
@import '~vuetify/src/styles/main.sass';
If you want your UI controls (links, switchs, sliders and form elements as button
, input
, select
, textarea
, fieldset
, legend
) in their default, disabled and active states to match, you need to control the text color
, border-color
and background-color
CSS values that are applied on ALL Vuetify components « as a all ». In Vuetify v.2.6 this is manage by src/styles/styles.scss
that loads src/styles/tools/*
and src/styles/settings/*
.
Any color that is trigged by Dark or Light Modes refers to Google's Material Design theming.
Sass variable | npm package file |
---|---|
$color-pack |
~/node_mobules/vuetify/src/styles/settings/_variables.scss |
$grey:() $shades:()
|
~/node_mobules/vuetify/src/styles/settings/_colors.scss |
$material-dark:() |
~/node_mobules/vuetify/src/styles/settings/_dark.scss |
$material-light:() |
~/node_mobules/vuetify/src/styles/settings/_light.scss |
- Border radius
- Flex
- Spacing margin and padding
Custom column gaps based on Vuetify's responsive $grid-gutter
variable for any Flex positionned elements.
// Hard values (duplicated from Vuetify)
$rv-grid-gutter-xs: 12px;
$rv-grid-gutter-sm: 20px;
$rv-grid-gutter-md: 24px;
$rv-grid-gutter-lg: 32px;
$rv-grid-gutter-xl: 40px;
// Create one reponsive Grid Gutter variable adapted for every breakpoints
--grid-gutter: #{$rv-grid-gutter-xs};
@media ($rv-sm-min) {
--grid-gutter: #{$rv-grid-gutter-sm};
}
@media ($rv-md-min) {
--grid-gutter: #{$rv-grid-gutter-md};
}
@media ($rv-lg-min) {
--grid-gutter: #{$rv-grid-gutter-lg};
}
@media ($rv-xl-min) {
--grid-gutter: #{$rv-grid-gutter-xl};
}
// Generate inline-margins between childs of `d-flex`
.flex-column-gap {
column-gap: var(--grid-gutter);
}
.flex-small-column-gap {
column-gap: calc(var(--grid-gutter) / 2);
}
.flex-button-column-gap {
column-gap: 8px;
> .v-btn.flex-grow-1 {
margin-left: 0;
margin-right: 0;
}
}
Container show vertical scrollbar when child overflows
// Hard values
--scrollbar-y-width: 10px; // from inspecting Chromio/Webkit browsers
--color-control-theme: lime; // ............. replace by your color
--color-control-contrast-theme: red; // ..... replace by your color
// styling
.scrollbar-y-container {
overflow-x: hidden; // prevent horizontal scroll
overflow-y: scroll;
max-height: 100%;
// cross-browser style hack: No Gecko/Firefox compatibility
&::-webkit-scrollbar {
width: var(--scrollbar-y-width);
background-color: transparent;
}
&::-webkit-scrollbar-thumb {
background-color: var(--color-control-theme);
border-radius: var(--scrollbar-y-width);
&:hover {
background-color: var(--color-control-contrast-theme);
}
}
}
Same logic as Bootsatrp (v4) Grid System with it's own methods of declaring Breakpoints where you have a <v-container fluid>
within a <v-row>
where the set of <v-col cols="12">
exists. We also have a <v-spacer>
to offset some containers if required.
XS | 0 | @media screen and (min-width:0) |
|
---|---|---|---|
SM | 600 | @media screen and (min-width:600px) |
|
MD | 960 | @media screen and (min-width:960px) |
|
LG | 1264 | @media screen and (min-width:1264px) |
|
XL | 1904 | @media screen and (min-width:1904px) |
|
XXL | <* class="container-fluid"> |
Vuetify comes with a mobileBreakpoint
variable to trigger the styles for its components to switch from « Mobile VS Desktop » layout.
This variable can then be computed in a template via the «isMobile
» statement to differentiate any mobile device viewport dimensions, both Tablets and Phones, from larger Computers browser viewport dimensions.
With the «isMobile
» we are missing a granular statement to differentiate Smartphones from other « mobile devices » (tablets). Therefor the «isMobilePhone
» statement below rely on the «xs
» breakpoint to get his true
statement.
<template> <fragment> <figure v-if="isMobile" :class="isMobilePhone ? 'is-mobile-phone' : 'is-mobile-tablet'"></figure> <artile v-if="isMobilePhone"> <section> @media XS : Smartphones only! </section> </artile> <artile v-if="!isMobilePhone"> <section v-if="isMobile"> @media SM ≥ MD : Tablets and reduced browser windows on computers. </section> <section v-if="!isMobile"> @media LG ≥ ∞ : Computer’s monitor screens. </section> </artile> </template><script> … computed: { isMobile() { return this.$vuetify.breakpoint.mobile; }, isMobilePhone() { const vbpName = this.$vuetify.breakpoint.name; let phoneBreakpoint = false; if (vbpName === "xs") { phoneBreakpoint = true; } return phoneBreakpoint; }, },
In this example, depending on the contexts where this component is imported, we may or not display multiple columns. Not that the :lg
declaration below is a classic interpolation condition.
<template>
…
<v-col
cols="12"
:md="{ 6: setDisplayOptionalColumn }"
:lg="setDisplayOptionalColumn ? 7 : null"
>…</v-col>
…
<v-col v-if="setDisplayOptionalColumn()">
<slot name="optional-column-container" />
</v-col>
…
</template>
<script>
…
props: {
isDisplayOptionalColumn: {
type: Boolean,
default: () => false,
},
},
methods: {
setDisplayOptionalColumn() {
return this.isDisplayOptionalColumn;
},
},
styles/_values.scss
styles/_sass-responsive-variables.scss
styles/responsive-variables.scss
plugins/vuetify.js
The values from this file can be mapped within other Sass and/or exported to JavaScript so no values from this Sass document are duplicated in our project making it THE SOURCE OF TRUTH to import where needed, like in Vuetify config file (vuetify.js
) where they then get exported within the breakpoint thresholds.
$breakpoints: (
"smBreakpoint": 600px,
"mdBreakpoint": 960px,
"lgBreakpoint": 1264px,
"xlBreakpoint": 1904px,
);
:export {
/**
* Remove quotes (required by Sass) so JavaScript won't read these as strings.
* Divide value by 1px to get a unitless value, stripping the "px". ex: 600px / 1px = 600
*/
@each $key, $value in $breakpoints {
#{unquote($key)}: $value / 1px;
}
}
@use "sass:map";
@import "_values";
$rv-breakpoint-sm: map.get($breakpoints, "smBreakpoint");
$rv-breakpoint-md: map.get($breakpoints, "mdBreakpoint");
$rv-breakpoint-lg: map.get($breakpoints, "lgBreakpoint");
$rv-breakpoint-xl: map.get($breakpoints, "xlBreakpoint");
/** ----------------------
* Media queries to be used within `Component.vue` file
* ex: `@media ($rv-min-md) {}`
*/
// Maximum viewport width (for smartphones)
$rv-max-xs: "max-width: " + ($rv-breakpoint-sm - 1px);
// Minimum viewport widths
$rv-min-sm: "min-width: " + $rv-breakpoint-sm;
$rv-min-md: "min-width: " + $rv-breakpoint-md;
$rv-min-lg: "min-width: " + $rv-breakpoint-lg;
$rv-min-xl: "min-width: " + $rv-breakpoint-xl;
/** ----------------------
* Breakpoint related variables
*/
$rv-grid-gutter-xs: 12px;
$rv-grid-gutter-sm: 20px;
$rv-grid-gutter-md: 24px;
$rv-grid-gutter-lg: 32px;
$rv-grid-gutter-xl: 40px;
$rv-font-size-xs: 1rem;
$rv-font-size-sm: 1.25rem;
$rv-font-size-xl: 2rem;
The following responsive variables are safe to use within any component since they will be compiled as CSS.
@import "_values";
@import "_sass-variables";
:root {
// Responsive Variables
--rv-font-size-base: #{$rv-font-size-xs};
--rv-grid-gutter: #{$rv-gutter-xs};
@media ($mainrv-sm-min) {
--rv-font-size-base: #{$rv-font-size-sm};
--rv-grid-gutter: #{$rv-gutter-sm};
}
@media ($mainrv-md-min) {
--rv-grid-gutter: #{$rv-gutter-md};
}
@media ($mainrv-lg-min) {
--rv-grid-gutter: #{$rv-gutter-lg};
}
@media ($mainrv-xl-min) {
--rv-font-size-base: #{$rv-font-size-xl};
--rv-grid-gutter: #{$rv-gutter-xl};
}
}
import Values from "@/styles/_values.scss";
export default new Vuetify({
breakpoint: {
thresholds: {
xs: Values.smBreakpoint,
sm: Values.mdBreakpoint,
md: Values.lgBreakpoint,
lg: Values.xlBreakpoint,
},
scrollBarWidth: 0,
mobileBreakpoint: "sm", // default: Larger then this = Desktop style
},
});