The goal of this document is to provide a high level overview of scss and bootstrap in relation to Intuition and provide you with resources to dig deeper.
SCSS turns CSS into a full blown language and an overwhelming number of features. We will primarily benefit from:
- Imports
- Variables
- Mixins
- Functions
SASS & SCSS are the same, SASS is just a different syntax (like coffeescript to javascript).
For an example of importing, variables and function see statement-charge-breakdown.vue
:
<style scoped lang="scss">
@import "src/styles/wyyerd_intuition.scss";
.chip {
background: $primary_1_;
border-radius: 1.2rem;
padding-left: 1.2rem;
padding-right: 1.2rem;
padding-top: 0.2rem;
padding-bottom: 0.2rem;
}
th {
color: color("gray");
}
</style>
For an example of nesting, selectors & combinators see statement-details.vue
:
<style scoped lang="scss">
.statement-details {
::v-deep tr {
border-bottom: 1px solid #dfe3e7;
&.total-row {
border-bottom: 0;
}
> td,
> th {
border-top: 0;
}
}
}
</style>
Here is a mixin defined in wyyerd_intuition.scss
@mixin noto_sans_h1($text-align: "") {
@if $text-align != "" {
text-align: $text-align;
}
font-weight: 300;
line-height: 64px;
font-size: 52px;
letter-spacing: 0.20000000298023px;
}
<template>
<div>
<label class="title">I'm primary color, aligned center, with a big ol' typeface</label>
</div>
<template>
<style scoped lang="scss">
@import "src/styles/wyyerd_intuition.scss";
.title {
// mixin defined in wyyerd_intuition.scss
@include noto_sans_h1("center");
color: $primary;
}
</style>
What else do we get access to when we import wyyerd_intuition.scss
? Navigate to the file and ctrl click to view.
@import "~bootstrap/scss/functions";
@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/mixins";
// Optional
@import "~bootstrap/scss/reboot";
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/images";
@import "~bootstrap/scss/code";
@import "~bootstrap/scss/grid";
- CSS Selectors
- CSS ' ', '>', '-', '+', '||' Combinators
- SASS Nesting
- SASS '&' Parent Selector
- Mixins
- Bootstrap 4 sass mixins cheat sheet
- Bootstrap 4 functions
- Vue ::v-deep Deep Selector
"Where should I put my styles?"
There are two primary areas we will be putting styles:
- The component. If you want a style to only affect a single component (and possibly it's children) it goes here.
wyyerd_intuition.scss
. If the style is overriding a bootstrap style, variable or defining a new color it should go here. Every color we use should be defined here.
We also have 2 other places, but usage of these will be less common:
-
main.scss
: This is where we put any styles that should apply to our app, multiple components, but aren’t bootstrap specific. -
overrides.scss
: This is where we lay down the hammer and resolve any CSS conflicts. In an ideal world this wouldn’t exist; however, since we have 3 CSS frameworks it does.
Bootstrap provides a grid system that enables us to easily define a layout and position elements in a responsive manner.
Careful styling the grid elements, as some changes can break an entire layout.
- For
b-col
you shouldn't mess with anything that changes the sizing, positioning, display etc. - For
b-row
changing the left - right padding
<b-row>
<b-col cols="8">Takes up 8 out of 12</b-col>
<b-col>Takes up 2 out of 12</b-col>
<b-col>Takes up 2 out of 12</b-col>
</b-row>
Grid supports:
-
Custom width columns
-
Equal width column
-
Multiline columns
-
Variable width content
-
Sizing columns for different display sizes
-
Offsetting columns
Utilities are classes that bootstrap provides for common things you would normally write styles for.
-
Borders
-
Colors, Text-Colors
-
Floats
-
Margins, Padding
-
Text alignment & styling
We use vee-validate
for form validation.
In order for a component to validate it needs to be wrapped in a validation-observer
and needs to have a ref
defined for us to access it. b-enhanced-form
already handles this, so any forms wrapped in a b-enhanced-form
don't need to worry about this.
b-enhanced-form
<template>
<validation-observer ref="observer">
<b-form ref="form">
<b-alert v-model="showError" variant="danger" dismissible>{{
displayError
}}</b-alert>
<slot />
</b-form>
</validation-observer>
</template>
Here is an example of how to validate all the validation-providers
inside of the observer:
const validationObs: InstanceType<typeof ValidationObserver> = this.$refs
.observer as InstanceType<typeof ValidationObserver>;
let isValid = await validationObs.validate();
Each input will need to be wrapped in a validation-provider
with a v-slot="v"
and rules
.
- Slot provides us with validation messaging, classes and more
- The rules is where we define rules for the input, see
rules.ts
.
To validate a b-form-group
- Add a
:class="v.classes"
binding to the input element. - Add a
<b-form-invalid-feedback>{{ v.errors[0] }}</b-form-invalid-feedback>
(or</b-form-valid-feedback>
) This will show or hide depending on what classes get set by the validator.
This is an excerpt from form-reset-password
demonstrating the above:
<validation-provider v-slot="v" name="Login" rules="required">
<b-form-group label="Login">
<b-form-select
v-model="form.loginId"
placeholder
:options="logins"
value-field="id"
:class="v.classes"
text-field="username"
>
<template v-slot:first>
<b-form-select-option :value="null" disabled
>-- Select Login --</b-form-select-option
>
</template>
</b-form-select>
<b-form-invalid-feedback>{{ v.errors[0] }}</b-form-invalid-feedback>
</b-form-group>
</validation-provider>
Examples of validation in our app:
-
form-reset-password
-
form-locate-response
-
b-enhanced-form
We use a library called v-maska for input masking. To add masking to an input add a v-maska
directive to the input and provide a string.
Here we can see it used by b-input-phone.vue
, which uses a computed string to set the mask:
<b-form-input
ref="input"
v-model="number"
v-maska="phoneMask"
type="tel"
:readonly="isReadonly"
:disabled="disabled"
:required="required"
:maxlength="dialCode === 1 ? 14 : 15"
:placeholder="placeholder"
@input="onInput"
></b-form-input>
...
computed: {
phoneMask(): string {
if (this.dialCode === 1) {
// Phone mask for North American numbers
return "(###) ###-####";
} else {
// Allow any digit for other numbers
return "#*";
}
},
}