solution • Vuetify templates - martindubenet/wed-dev-design GitHub Wiki
[ Vue.js coding ][ Vuetify templates ][ Vue.js setup for Vuetify ][ Vue headless CMS site ][ Vue Tailwind json ]
-
Features guide
- Sass.scss variables
-
Programmatic scrolling via
goTo().
- Components
-
Application to help create the main
<v-app.vuefile 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-Tooltipwhile 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.scssstyles/_sass-responsive-variables.scssstyles/responsive-variables.scssplugins/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
},
});
