Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save iamsaief/4efa303b852a7ff28b3050d32f006a37 to your computer and use it in GitHub Desktop.
Save iamsaief/4efa303b852a7ff28b3050d32f006a37 to your computer and use it in GitHub Desktop.
CSS Framework Building Blocks | Typography, Utility classes, Breakpoints with SASS/SCSS

CSS Framework Building Blocks | Typography, Utility classes, Breakpoints with SASS/SCSS

🎯 Utility Classes

✨ For this demo utility classes are generated only for padding. But the possibilities are endless; using this demo you can extend it even further as much as you want.

👨‍💻 The classes are named using the format {property}{sides}-{size} for default (xs) and {property}{sides}-{breakpoint}-{size} for sm, md, lg, xl, and xxl. To enable responsive utility just put "responsive": true for that particular property. e.g. The classes are, .p-5, .p-sm-5, .p-md-5, .p-lg-5, p-xl-5, .p-xxl-5 and so on.

Classes follow a 5-grid structure.

.p-* = 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,.... and etc. 
* = 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 in 'px'

🎯 Breakpoints

Generates mobile first media queries from xs to xxl, so you dont have to write explicitly @media (min-width: 768px) { .... } instead you just include mixins like this @include breakpoint-min(lg) { ... }

$breakpoints: ( xs: 0, sm: 576px, md: 768px, lg: 992px, xl: 1200px, xxl: 1400px, );

🎯 Fluid Typography classes

✨ The idea of fluid typography has been around in application development for a while, but to make this work in the browser, developers had to use a variety of solutions.

👨‍💻 Depending on the viewport width, fluid typography adjusts nicely between the minimum and maximum value. Typically, it starts at a minimum value and stays constant until a certain screen width point, at which time it starts to rise. When it hits a maximum value at a different screen width, it keeps going with that maximum value.

👨‍🏫  For instance, if we wanted our font size to be between 2em and 3em, where 2em is the minimum size at the smallest viewport width of 320px and 3em is the maximum size at the highest viewport width of 1366px. Then our equation will look like this - font-size: calc(2em + 1 * (100vw - 320px) / 1046);

@media (min-width: 1366px){
  .fluid-h2 {
      font-size: 3em;
  }
}

@media (min-width: 320px){
  .fluid-h2 {
      font-size: calc(2em + 1 * (100vw - 320px) / 1046);
  }
}

.fuild-h2 {
    font-size: 2em;
    font-weight: 600;
    line-height: 1.21;
}
// Media Query Mixins
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px,
);
// @include breakpoint-min(lg) { ... }
@mixin breakpoint-min($size) {
@if $size == sm {
@media (width >= map-get($map: $breakpoints, $key: "sm")) {
@content;
}
} @else if $size == md {
@media (width >= map-get($map: $breakpoints, $key: "md")) {
@content;
}
} @else if $size == lg {
@media (width >= map-get($map: $breakpoints, $key: "lg")) {
@content;
}
} @else if $size == xl {
@media (width >= map-get($map: $breakpoints, $key: "xl")) {
@content;
}
} @else if $size == xxl {
@media (width >= map-get($map: $breakpoints, $key: "xxl")) {
@content;
}
} @else {
@media (width >= #{$size}px) {
@content;
}
}
}
// @include breakpoint-max(md) { ... }
@mixin breakpoint-max($size) {
@if $size == sm {
@media (width <= (map-get($map: $breakpoints, $key: "sm") - 0.02)) {
@content;
}
} @else if $size == md {
@media (width <= (map-get($map: $breakpoints, $key: "md") - 0.02)) {
@content;
}
} @else if $size == lg {
@media (width <= (map-get($map: $breakpoints, $key: "lg") - 0.02)) {
@content;
}
} @else if $size == xl {
@media (width <= (map-get($map: $breakpoints, $key: "xl") - 0.02)) {
@content;
}
} @else if $size == xxl {
@media (width <= (map-get($map: $breakpoints, $key: "xxl") - 0.02)) {
@content;
}
} @else {
@media (width <= #{$size}px) {
@content;
}
}
}
// @include breakpoint-minmax(md, lg) { ... }
@mixin breakpoint-minmax($sizemin, $sizemax) {
@if ($sizemin == "sm" and $sizemax == "md") {
@media (map-get($map: $breakpoints, $key: "sm") <= width <= map-get($map: $breakpoints, $key: "md")) {
@content;
}
} @else if ($sizemin == "md" and $sizemax == "lg") {
@media (map-get($map: $breakpoints, $key: "md") <= width <= map-get($map: $breakpoints, $key: "lg")) {
@content;
}
} @else if ($sizemin == "lg" and $sizemax == "xl") {
@media (map-get($map: $breakpoints, $key: "lg") <= width <= map-get($map: $breakpoints, $key: "xl")) {
@content;
}
} @else if ($sizemin == "xl" and $sizemax == "xxl") {
@media (map-get($map: $breakpoints, $key: "xl") <= width <= map-get($map: $breakpoints, $key: "xxl")) {
@content;
}
} @else {
@media (#{$sizemin}px <= width <= #{$sizemax}px) {
@content;
}
}
}
// Media Query Mixins
$breakpoints: (
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px,
xxl: 1400px,
);
// @include breakpoint-min(lg) { ... }
@mixin breakpoint-min($size) {
@if $size == sm {
@media (width >= map-get($map: $breakpoints, $key: "sm")) {
@content;
}
} @else if $size == md {
@media (width >= map-get($map: $breakpoints, $key: "md")) {
@content;
}
} @else if $size == lg {
@media (width >= map-get($map: $breakpoints, $key: "lg")) {
@content;
}
} @else if $size == xl {
@media (width >= map-get($map: $breakpoints, $key: "xl")) {
@content;
}
} @else if $size == xxl {
@media (width >= map-get($map: $breakpoints, $key: "xxl")) {
@content;
}
} @else {
@media (width >= #{$size}px) {
@content;
}
}
}
// @include breakpoint-max(md) { ... }
@mixin breakpoint-max($size) {
@if $size == sm {
@media (width <= (map-get($map: $breakpoints, $key: "sm") - 0.02)) {
@content;
}
} @else if $size == md {
@media (width <= (map-get($map: $breakpoints, $key: "md") - 0.02)) {
@content;
}
} @else if $size == lg {
@media (width <= (map-get($map: $breakpoints, $key: "lg") - 0.02)) {
@content;
}
} @else if $size == xl {
@media (width <= (map-get($map: $breakpoints, $key: "xl") - 0.02)) {
@content;
}
} @else if $size == xxl {
@media (width <= (map-get($map: $breakpoints, $key: "xxl") - 0.02)) {
@content;
}
} @else {
@media (width <= #{$size}px) {
@content;
}
}
}
// @include breakpoint-minmax(md, lg) { ... }
@mixin breakpoint-minmax($sizemin, $sizemax) {
@if ($sizemin == "sm" and $sizemax == "md") {
@media (map-get($map: $breakpoints, $key: "sm") <= width <= map-get($map: $breakpoints, $key: "md")) {
@content;
}
} @else if ($sizemin == "md" and $sizemax == "lg") {
@media (map-get($map: $breakpoints, $key: "md") <= width <= map-get($map: $breakpoints, $key: "lg")) {
@content;
}
} @else if ($sizemin == "lg" and $sizemax == "xl") {
@media (map-get($map: $breakpoints, $key: "lg") <= width <= map-get($map: $breakpoints, $key: "xl")) {
@content;
}
} @else if ($sizemin == "xl" and $sizemax == "xxl") {
@media (map-get($map: $breakpoints, $key: "xl") <= width <= map-get($map: $breakpoints, $key: "xxl")) {
@content;
}
} @else {
@media (#{$sizemin}px <= width <= #{$sizemax}px) {
@content;
}
}
}
/* Fluid Typography Sass Mixin */
@mixin fluid-type($properties, $min-vw, $max-vw, $min-value, $max-value) {
@each $property in $properties {
#{$property}: $min-value;
}
@media (min-width: $min-vw) {
@each $property in $properties {
#{$property}: calc(
#{$min-value} + #{strip-unit($max-value - $min-value)} * (100vw - #{$min-vw}) / #{strip-unit($max-vw - $min-vw)}
);
}
}
@media (min-width: $max-vw) {
@each $property in $properties {
#{$property}: $max-value;
}
}
}
@function strip-unit($number) {
@if type-of($number) == 'number' and not unitless($number) {
@return $number / ($number * 0 + 1);
}
@return $number;
}
// ($name, $max, $min, $lh, $fw)
$fluidTypography: (
'h1' 4em 3em 1.09 600,
'h2' 3em 2em 1.21 600,
'h3' 2em 1.5em 1.31 600,
'h4' 1.5em 1.25em 1.42 600,
'h5' 1.25em 1.125em 1.5 600,
'h6' 1.125em 1em 1.5 600,
'18' 1.125em 1em 1.56 400,
'16' 1em null 1.62 400,
'14' 0.875em null 1.71 400,
'13' 0.815em null 1.69 400
);
/* Utility Class */
@each $name, $max, $min, $lh, $fw in $fluidTypography {
.fluid-#{$name} {
font-weight: #{$fw};
line-height: #{$lh};
@if $min != null {
@include fluid-type(font-size, 320px, 1366px, $min, $max);
} @else {
font-size: $max;
}
}
}
@import './breakpoints';
/* Generating Responsive Utility Classes with SASS/SCSS */
// spacing
$base: 5px !default;
$spacing: (
"0": 0,
"1": $base * 1,
"2": $base * 2,
"3": $base * 3,
"4": $base * 4,
"5": $base * 5,
"6": $base * 6,
"7": $base * 7,
"8": $base * 8,
"9": $base * 9,
"10": $base * 10,
"11": $base * 11,
"12": $base * 12
);
$utilities: (
'padding': (
'prefix': 'p',
'values': $spacing,
'responsive': true,
),
'padding-inline': (
'prefix': 'px',
'values': $spacing,
'responsive': true,
),
'padding-block': (
'prefix': 'py',
'values': $spacing,
'responsive': true,
),
'padding-block-start': (
'prefix': 'pt',
'values': $spacing,
'responsive': true,
),
'padding-inline-end': (
'prefix': 'pe',
'values': $spacing,
'responsive': true,
),
'padding-block-end': (
'prefix': 'pb',
'values': $spacing,
'responsive': true,
),
'padding-inline-start': (
'prefix': 'ps',
'values': $spacing,
'responsive': true,
),
);
// Media Query Mixins
$screenSizes: (sm, md, lg, xl, xxl);
// Generate Utility Classes
@each $property, $map in $utilities {
$prefix: map-get($map, 'prefix');
$values: map-get($map, 'values');
$responsive: map-get($map, 'responsive');
@each $k, $v in $values {
@if ($k == 'default') {
.#{$prefix} {
#{$property}: #{$v};
}
} @else {
.#{$prefix}-#{$k} {
#{$property}: #{$v};
}
}
// Responsive
@if $responsive == true {
@each $b in $screenSizes {
@include breakpoint-min($b) {
@if ($k == 'default') {
.#{$prefix}-#{$b} {
#{$property}: #{$v} !important;
}
} @else {
.#{$prefix}-#{$b}-#{$k} {
#{$property}: #{$v} !important;
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment