Try   HackMD

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

A convenient Scss library for quick use.
If you prefer Scss development and need to collaborate with UI/UX designers following the guidelines, this Library is perfect for you!
Of course, you can also easily extend and maintain this framework!

Source Code Location

You can use it directly in your project. It supports Vue, React, Nuxt, and Next.
If there are any improvements that can be made, feel free to send a PR after messaging me.
https://github.com/fortes1219/scss-base-library

npm install npm run dev

Since the author of SassMeister announced on Twitter that it is shut downif the website is not accessible, it is recommended to use https://sass.js.org/ for preview.

Before reading this document, please be aware of a few things.

1. Consider the Technical Choices within Your Team

If your project does not need to consider changing skins, you can continue to use TailwindCSS, Stylus, bootstrap, or other methods you are familiar with or consider faster.

2. Content Will Be Continuously Updated

As browser versions update, the content will be revised periodically. Since I use this library regularly, I might add new features, such as the recently usable :has().

3. Treat UI/UX Designers Well

This library essentially needs to follow themes defined with tools like Figma or XD. If you have a designer who can accurately specify each theme color, typography, spacing, and easing, treat them well. It will greatly help with your skin-changing tasks and redesigns.

Outline

Applicable Version: Sass 1.50.1 +
Testing Tool: Recommended to use Sass.js

Architecture Diagram

It is recommended to open the image in a new window: https://i.imgur.com/J3hNFJT.png

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Theme Color

Live Code: https://codepen.io/fortes-huang/pen/gOdJOWG

Path: src/style/config/_theme.scss

There are two ways to extract theme colors defined in Figma.

Class Name

Automatically generate CSS through map traversal and apply it directly.

$theme-prefix: 'nt'; // Custom prefix
<div class="my-text nt-primary-900"></div>

The generated CSS will look like this:

.nt-primary-default {
  color: #6a675d;
}

.nt-primary-300 {
  color: #a09d92;
}

.nt-primary-200 {
  color: #bdb9ae;
}

.nt-primary-100 {
  color: #d9d5ca;
}

.nt-primary-gradient {
  background: linear-gradient(#baa671, #d4be81);
}

.nt-secondary-default {
  color: #85754a;
}

useColor

Apply it to any color or background.

.my-typography {
  // Corresponding to primary-900: #15130E
  color: useColor(primary, 900);
}

.sample-gradient {
  // Corresponding to semantic-dramatic: linear-gradient(#BAA671, #D4BE81)
  background: useColor(semantic, dramatic);
}

Source Code Construction

// Theme name prefix, can be replaced according to different project templates
$theme-prefix: 'nt';

// Theme map of all color swatches
$themeColor: (
  primary: (
    default: #6a675d,
    900: #15130e,
    800: #2a2823,
    700: #39362d,
		...
  ),(...),(...)
);

@each $category, $color in $themeColor {
  @each $variant, $value in $color {
    @if type-of($value) == map {
      @each $subvariant, $subvalue in $value {
        .#{$theme-prefix}-#{'' + $category}-#{$subvariant} {
          @if str-index(inspect($subvalue), 'linear-gradient') {
            background: $subvalue;
          } @else {
            color: $subvalue;
          }
        }
      }
    } @else {
      .#{$theme-prefix}-#{'' + $category}-#{$variant} {
        @if str-index(inspect($value), 'linear-gradient') {
          background: $value;
        } @else {
          color: $value;
        }
      }
    }
  }
}

// get colors
@function useColor($category, $variant: 'default') {
  $color: map-get(map-get($themeColor, $category), $variant);
  @return $color;
}

Typography

Here is an overview of how to define the typographic system in your project.

Live Code: https://codepen.io/fortes-huang/pen/XWPwbXE

heading

heading text h1 to h6

/*== h1~h6 ==*/

$headings: (
  h1: (
    font-size: 40px,
    line-height: 48px
  ),
  h2: (
    font-size: 32px,
    line-height: 40px
  ),
  h3: (
    font-size: 26px,
    line-height: 28px
  ),
  h4: (
    font-size: 21px,
    line-height: 21px
  ),
  h5: (
    font-size: 18px,
    line-height: 18px
  ),
  h6: (
    font-size: 16px,
    line-height: 16px
  )
);

/*== create h1 to h6 ==*/

@each $heading, $properties in $headings {
  #{$heading} {
    @each $property, $value in $properties {
      #{$property}: $value;
    }
  }
}

/*== result ==*/

h1 {
  font-size: 40px;
  line-height: 48px;
}

h2 {
  font-size: 32px;
  line-height: 40px;
}

h3 {
  font-size: 26px;
  line-height: 28px;
}

font-size & clamp

Font size settings, color matching, and specifying breakpoints to apply text-overflow: ellipsis

path:src/style/config/_typography.scss

Usage of Font Sizes:

<div class="my-article nt-text-sm">This is a small size text</div>
<div class="my-article nt-text-base">This is a default size text</div>
<div class="my-article nt-text-lg right">This is a slightly larger text, aligned to the right</div>

Usage with Font Colors:

<div class="my-article nt-text-sm">
  This is a text, <span class="nt-accent-600">this part will be colored</span>
</div>

Specify Text Overflow at the Nth Line:

<div class="my-article nt-text-sm clamp-3">
  This text will overflow at the third line, now the first line<br />
  I am the second line<br />
  I am the third line<br />
  I am the possibly hidden fourth line
</div>

Preview Effect

Principles and Source Code

Here we use mixins for text overflow.

path:src/style/config/_mixins.scss

// src/style/config/_mixins.scss

/*== Enable text overflow for a specified number of lines ==*/
// width is used to set a fixed width for the container, accepting fit-content
// -webkit-line-clamp: $line-count = specifies the number of lines to show "..."

%extendEllipsis {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  height: auto;
  line-height: inherit;
  overflow: hidden;
}

@mixin useEllipsis($width, $line-count) {
  width: $width;
  -webkit-line-clamp: $line-count;
  overflow: hidden;
  @extend %extendEllipsis;
}

useEllipsis is used in typography.scss

// map table for font sizes
$textConfig: (
  max-clamp: 10,
  size-prefix: (
    'xs',
    'sm',
    'base',
    'lg',
    'xl',
    '2xl'
  ),
  size: (
    12px,
    14px,
    16px,
    18px,
    21px,
    24px
  ),
  default-color: #ffffff
);

// give variables for prefix & size
$textPrefixList: map-get($textConfig, size-prefix);
$textSizeList: map-get($textConfig, size);

@each $size in $textSizeList {
  $index: index($textSizeList, $size);
	// theme-prefix default was 'nt'
  .#{$theme-prefix}-text-#{nth($textPrefixList, $index)} {
    font-size: px-to-rem($size);
    color: map-get($textConfig, default-color);
    @for $i from 1 through map-get($textConfig, max-clamp) {
      &.clamp-#{$i} {
        @include useEllipsis(null, $i);
      }
    }
    @each $align in 'left', 'right', 'center' {
      &.#{$align} {
        text-align: #{$align};
      }
    }
  }
}
// result

.nt-text-xs {
  font-size: 0.75rem;
  color: #ffffff;
}

.nt-text-xs.clamp-2 {
  -webkit-line-clamp: 2;
  overflow: hidden;
}

.nt-text-xs.clamp-3 {
  -webkit-line-clamp: 3;
  overflow: hidden;
}

Adaptor: pixel & rem

A method to convert px to rem or rem to px.

path:src/style/config/_remAdaptor.scss

Applicable Scenarios

If your project and designer have agreed to use proportional scaling to display the mobile site appearance, you will likely need to set up something like this for your project:

https://github.com/fortes1219/scss-base-library/blob/master/rem.js

This script sets the font-size to 100px at a default resolution of 390x884 (iPhone 12/13). The purpose is to dynamically change the body font-size so that the UI's width and height are calculated in rem units, facilitating proportional scaling.

Usage:

// rem.js

function setRootFontSize(doc, win) {
  const clientWidth = win.innerWidth || doc.documentElement.clientWidth;
  if (!clientWidth) return;

  // Check if the device is a PC
  const isPC = win.matchMedia("(min-width: 1024px)").matches;
  if (isPC) {
    doc.documentElement.style.fontSize = "72px";
    return;
  }

  // Set font size based on screen width
  const baseFontSize = 100 * (clientWidth / 390);
  if (clientWidth >= 540) { // Modify the device width limit as needed
    doc.documentElement.style.fontSize = "100px";
  } else {
    doc.documentElement.style.fontSize = baseFontSize + "px";
  }
}

export default setRootFontSize;

// main.js
import debounce from "lodash/debounce";
import setRootFontSize from "./rem.js";

// Dynamic rem handling with lodash debounce to prevent resize jitter
document.addEventListener("DOMContentLoaded", () =>
  setRootFontSize(document, window)
);

const debouncedSetRootFontSize = debounce(
  () => setRootFontSize(document, window),
  10
);
window.addEventListener("resize", debouncedSetRootFontSize);
window.addEventListener("orientationchange", debouncedSetRootFontSize);
// The actual converted value will change based on the body font-size in rem.js
.my-class {
  width: px-to-rem(100px); // 1rem
  font-size: px-to-rem(2rem); // 200px
}

Source Code:

@use 'sass:math';

// Proportional scaling layout will be set with baseRemRate = 100 * (current device width / 750)
// Here the base rate of 100px can be seen as a 100% scale

$baseRemRate: 100px;

@function strip-unit($number) {
  @return math.div($number, ($number * 0 + 1));
}

@function px-to-rem($value) {
  // unit unit will find out if $value includes a unit
  @if unit($value) == 'px' {
    @return math.div(strip-unit($value), strip-unit($baseRemRate)) * 1rem;
  } @else if unit($value) == 'rem' {
    @return strip-unit($value) * $baseRemRate;
  } @else {
    @error "Invalid unit. Only 'px' and 'rem' units are supported.";
  }
}

Mixin: flexBox

path:src/style/config/_flexbox.scss

Live Code (codepen): https://codepen.io/fortes-huang/pen/OJVyVxw

/* src/style/config/_flexbox.scss */
@mixin flexBox($direction, $alignItems, $justifyContent) {
  display: flex;
  flex-direction: $direction;
  @if ($alignItems != null) {
      align-items: $alignItems;
  }
  @if ($justifyContent != null) {
      justify-content: $justifyContent;
  }
}

/* usage */

.my-element1 {
    @include flexBox(row, null, null);
}

.my-element2 {
    @include flexBox(row, center, space-between);
}

.my-element3 {
    @include flexBox(column, flex-end, center);
}

result:

.my-element1 {
  display: flex;
  flex-direction: row;
}

.my-element2 {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}

.my-element3 {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: center;
}

Generally, there is no need to set flex-shrink (shrink ratio), flex-basis (initial width).

If you need to make elements stick to the left and right edges of the parent container after wrapping, using Grid is more ideal.

This is because flexbox is designed to solve the layout problem with a "single row" premise. Using only flexbox makes the code relatively complex to maintain.

You can refer to the following flexList to see what conditions are needed.

flexList

Using flexbox for list items, you need to specify the number of columns in each row, the arrangement, and the width and height of the child elements.

row-type

Live Code: https://codepen.io/fortes-huang/pen/jOvKZOr

<h2>row:</h2>
<div class="flx is-row is-list sp-between wrap w-50vw" data-border="all">
  <span>1</span>
  <span>2</span>
  <span>3</span>
  ...
	...
</div>
@mixin flexListItem($rows, $margin, $unit) {
  // The width calculation must offset margin-right because it is 100/$rows, 
  // so it needs to add 20% of the margin in the end
  $mod: (100 / $rows) / 100;
  $mod: (100 / $rows) / 100;
  width: calc((100% / #{$rows}) - #{$margin + $unit} + (#{$margin} * #{$mod + $unit}));
  margin-right: #{$margin + $unit};
  margin-bottom: #{$margin + $unit};
  &:nth-child(#{$rows}n) {
    margin-right: 0;
  }
}

span {
  @include flexBox(row, center, center);
  @include flexListItem(10, 10, 'px');
  height: 120px;
  background-color: #333;
  color: #fff;
}

.flx {
  display: flex;
  width: 100%;
  box-sizing: border-box;
  
  &.is-list::after {
    content: '';
    display: flex;
    flex: 1;
  }
// ...
// ...
}

Regarding the implementation principle, let's look at an example:

Suppose the width of each child element card is calc(100%/5), using space-between to align the container left and right:

Here, align-content: start is used in .wrap, preventing the height between two lines from being increased after wrapping.

Settings to remove the margin for the last element (5n) of each row:

.child-element {
  @include flexBox(row, center, center);
  // The width calculation must offset the margin-right of 10px because it is 100/5, so it needs to add 20% of 10px = 2px
  width: calc((100%/5) - 10px + 2px);
  height: 120px;
  margin-right: 10px; // In principle, the margin must be deducted from the calc
  margin-bottom: 10px;
  background-color: #333;
  color: #fff;
  // Every 5th element will be the last one in the row
  &:nth-child(5n) { 
    margin-right: 0;
  }
}

If there is only one element in the second row, it's fine, but if there are more


space-between will separate the two elements in the second row, and it won't work for just one element.

The more extra space, the larger the gap.

Therefore, we use Pseudo Element: after to fill the unhandled space:

.flx {
  display: flex;
  width: 100%;
  box-sizing: border-box;
  // Set ::after as a decoration block with grow = 1, shrink = 1, and basis = auto
  &::after {
    content: '';
    display: flex;
    flex: 1;
  }
}

And add the mixin for child element list items:

@mixin flexListItem($rows, $margin, $unit) {
  // $rows: number of columns in a row, width: calc(100%/$rows)
  // $margin: margin width, represented by $mod
  // $unit: unit for margin
  $mod: (100 / $rows) / 100;
  width: calc((100% / #{$rows}) - #{$margin + $unit} + (#{$margin} * #{$mod + $unit}));
  margin-right: #{$margin + $mod};
  margin-bottom: #{$margin + $mod};
  &:nth-child(#{$rows}n) { // nth-child(number + n) means it applies every n elements
    margin-right: 0;
  }
}

span {
  @include flexBox(row, center, center);
  @include cards(5, 5, 'px');
  height: 120px;
  background-color: #333;
  color: #fff;
}

This way, you can smoothly customize the list regardless of the number of child elements.

column-type

Live Code: https://codepen.io/fortes-huang/pen/VwGELJq

If flex-direction: column is the basis, another method will be used, and justify-content must be flex-start.

<h2>column:</h2>
<div class="flx is-col is-list wrap w-50vw h-100vh" data-border="all">
  <span>1</span>
  <span>2</span>
  <span>3</span>
	...
	...
</div>
@mixin flexColumnListItem($rows, $margin, $unit) {

  $mod: (100 / $rows) / 100;
  height: calc((100% / #{$rows}) - #{$margin + $unit} + (#{$margin} * #{$mod + $unit}));
  margin-right: #{$margin + $unit};
  margin-bottom: #{$margin + $unit};
  &:nth-child(#{$rows}n) {
      margin-bottom: 0;
  }
}

span {
  @include flexBox(row, center, center);
  @include flexColumnListItem(4, 10, 'px');
  width: 120px;  
  background-color: #333;
  color: #fff;
}

To center the child elements, add align-content: center to the parent container, like this:

Grid List

path:src/style/config/_grid.scss

Live Code (codepen): https://codepen.io/fortes-huang/pen/wvEYOPG

Usage:

.sample-list {
  @include gridList(6, 10px, 100px);
  width: 600px;
}

Source Code:

The way to write a grid list:
$rows => Number of elements in one row
$gap => Spacing between elements
$autoSize => Used to specify the minimum height of each element, accepting 'auto' / null / any unit height

When the content height of a specific element in a row is higher than other elements, it will automatically stretch the height of all sibling elements in that row.


@mixin gridList($rows, $gap, $autoSize) {
  display: grid;
  grid-template-columns: repeat($rows, 1fr);
  grid-gap: $gap;
  @if $autoSize == 'auto' or $autoSize == null {
    grid-auto-rows: minmax(min-content, max-content);
  } @else {
    grid-auto-rows: minmax($autoSize, max-content);
  }
}

Conclusion

In conclusion, this SCSS library provides a robust and flexible foundation for building scalable and maintainable styles in your projects. By standardizing configurations for colors, typography, spacing, and responsive design, you can ensure consistency and efficiency in your development process. We hope you find these tools and techniques useful in your work.

Future Work

We are continuously working to improve this library. Future updates may include additional mixins, extended support for new CSS features, and more examples to demonstrate best practices. Stay tuned for more enhancements and feel free to contribute your ideas.

Acknowledgments

We would like to thank all the contributors and community members who have provided feedback and suggestions. Your support and collaboration are invaluable to the success of this project.

tags: Scss Scss Library Scss Optimization, SCSS best practices