Design System Core Spot Manager

  1. Start
  2. Setup Bootstrap
  3. Schriften
  4. Mehrfachauswahl
  5. Icons
  6. Diagramme
  7. Lightbox

Hier finden sich alle Komponenten für das System inkl. benötigtem Code.

Nach dem Import von bootstrap-setup.scss bzw. vor dem Import der Komponenten, muss der erste Import components/site/site.scss sein. Hier sind globale Sass-Variablen definiert.

Die Reihenfolge sollte also in etwa wie folgt aussehen.

  1. […]
  2. bootstrap-setup.scss
  3. components/site/site.scss
  4. components/**/*.scss
  5. […]

components/site/site.scss kann natürlich auch aus den Komponenten entfernt und von anderer Stelle importiert werden. Dass hier eine Komponente verwendet wurde, dient nur der Dokumentation.

Setup Bootstrap

Da neue Farben für das Farbsystem definiert wurde und das momentan eher schlecht als recht geht, ist kein weiterer Import des Bootstrap-Hauptmoduls nötig, da in unserem Setup alle Untermodule bereits importiert werden. Siehe Dokumentation.

~bootstrap ist ein in vite.config.js definierter Alias und muss dementsprechend ersetzt oder definiert werden.

@use 'sass:color';
@use "sass:map";
@import "~bootstrap/scss/functions";

////// Colors
//////

$gray-100:  #f6f6f6;
$gray-300:  #ececec;
$gray-400:  #cfcfcf;
$gray-500:  #aaa;
$gray-600:  #888;

$green:     #11BF22;
$orange:    #f29200;
$red:       #c80707;

$primary:       #0b2e53;
$secondary:     #888;
$warning:       $orange;
$accent:        #f29200;
$gray:          $gray-600;
$material:      #cbb491;
$not-material:  #888;
$open-material: #c80707;

$border-color:  $gray-400;

$focus-ring-color:      rgba($accent, .2);
$component-active-bg:   $accent;


////// Base
//////

$body-color: $primary;


////// Typographie
//////

$font-family-sans-serif:    Roboto, sans-serif;
$font-size-base:            .875rem;
$font-size-xs:              $font-size-base * .78571429;
$font-size-sm:              $font-size-base * 0.85714286;
$font-size-lg:              $font-size-base * 1.14285714;
$font-size-xl:              $font-size-base * 1.42857143;
$font-size-xxl:             $font-size-base * 1.71428571;

$line-height-base:          1.3;

$link-color:                $primary;
$link-hover-color:          $secondary;


//// Headings

$h1-font-size:          $font-size-base * 2;
$h2-font-size:          $font-size-base * 1.5;
$h3-font-size:          $font-size-base * 1.25;

$headings-font-weight:  700;


////// Forms
//////

////// Buttons

$input-btn-padding-y:   .5rem;
$input-btn-padding-x:   1rem;
$btn-border-radius:     0;
$btn-border-radius-sm:  0;
$btn-border-radius-lg:  0;
$btn-font-weight:       500;
$btn-padding-y:         .5rem;
$btn-padding-x:         1rem;

$input-btn-padding-y-sm:      .25rem;
$input-btn-padding-x-sm:      .5rem;

$input-btn-padding-y-lg:      1rem;
$input-btn-padding-x-lg:      1.25rem;
$input-font-size-lg:          $font-size-lg;


///// Labels

$form-label-color:      $secondary;
$form-label-font-size:  $font-size-xs;


///// Inputs

$input-bg:                      $gray-100;
$input-border-color:            $gray-100;
$input-border-radius:           0;
$input-border-radius-sm:        0;
$input-border-radius-lg:        0;
$input-padding-y:               $input-btn-padding-y;
$input-padding-x:               $input-btn-padding-x;
$input-padding-y-sm:            $input-btn-padding-y-sm;
$input-padding-x-sm:            $input-btn-padding-x-sm;
$input-placeholder-color:       $gray-400;
$input-disabled-bg:             darken($gray-300, 5%);
$input-disabled-border-color:   darken($gray-300, 5%);
$input-disabled-color:          rgba($primary, 0.6);


///// Selects

$form-select-bg:                $gray-100;
$form-select-border-color:      $gray-100;
$form-select-border-radius:     0;
$form-select-padding-y:         $input-padding-y;
$form-select-padding-x:         $input-padding-x;
$form-select-bg-position:       right ($form-select-padding-x * 0.5) center;
$form-select-indicator-padding: $form-select-padding-x * 2;
$form-select-disabled-color:    rgba($primary, 0.7);

$form-select-padding-y-sm:      $input-padding-y-sm;
$form-select-padding-x-sm:      $input-padding-x-sm;


///// Checkboxes + radios

$form-check-input-border-radius: 0;
$form-check-input-checked-bg-color: $primary;


///// Breadcrumbs
/////

$breadcrumb-active-color:   $gray-600;
$breadcrumb-divider:        quote('|');
$breadcrumb-divider-color:  $gray-600;


///// Navs
/////

$nav-link-padding-y:                0.5rem;
$nav-underline-gap:                 2.5rem;
$nav-underline-border-width:        1px;
$nav-underline-link-active-color:   $orange;


///// Tables
/////

$table-cell-padding-y:      .5rem;
$table-cell-padding-x:      1rem;
$table-cell-padding-y-sm:   .375rem;
$table-cell-padding-x-sm:   .75rem;

$table-color:               $primary;

$table-hover-color:         #fff;
$table-hover-bg:            $accent;
$table-striped-bg:          $gray-100;
$table-striped-order:       even;


///// Progress bars
/////

$progress-height: 01.25rem;
$progress-border-radius: 0;


///// Modals
/////

$modal-content-border-radius:   0;
$modal-sm:                      400px;
$modal-md:                      700px;


///// Accordions
/////

$accordion-icon-color:        $accent;
$accordion-icon-active-color: $accent;


////// Custom maps + utilities
//////

///// Bootstrap variables + maps

@import "~bootstrap/scss/variables";
@import "~bootstrap/scss/variables-dark";


///// Custom maps

// Custom colors
$theme-colors: map.merge($theme-colors, (
    "accent": $accent,
    "gray": $gray,
    "medium-light-gray": $gray-300,
    "light-gray": $gray-100,
    "material": $material,
    "not-material": $not-material,
    "open-material": $open-material,
));


///// Bootstrap maps, mixins + utilities

@import "~bootstrap/scss/maps";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/utilities";


///// Add accent color to more maps …

// Light mode
$custom-colors-text: (
    "accent": $accent,
    "material": $material,
    "not-material": $not-material,
    "open-material": $open-material,
);
$custom-colors-bg-subtle: (
    "accent": $accent,
    "material": $material,
    "not-material": $not-material,
    "open-material": $open-material,
);
$custom-colors-border-subtle: (
    "accent": $accent,
    "material": $material,
    "not-material": $not-material,
    "open-material": $open-material,
);

$theme-colors-text: map.merge($theme-colors-text, $custom-colors-text);
$theme-colors-bg-subtle: map.merge($theme-colors-bg-subtle, $custom-colors-bg-subtle);
$theme-colors-border-subtle: map.merge($theme-colors-border-subtle, $custom-colors-border-subtle);

// Dark mode
$custom-colors-text-dark: (
    "accent": $accent,
    "material": $material,
    "not-material": $not-material,
    "open-material": $open-material,
);
$custom-colors-bg-subtle-dark: (
    "accent": $accent,
    "material": $material,
    "not-material": $not-material,
    "open-material": $open-material,
);
$custom-colors-border-subtle-dark: (
    "accent": $accent,
    "material": $material,
    "not-material": $not-material,
    "open-material": $open-material,
);

$theme-colors-text-dark: map.merge($theme-colors-text-dark, $custom-colors-text-dark);
$theme-colors-bg-subtle-dark: map.merge($theme-colors-bg-subtle-dark, $custom-colors-bg-subtle-dark);
$theme-colors-border-subtle-dark: map.merge($theme-colors-border-subtle-dark, $custom-colors-border-subtle-dark);


///// Utilities

$utilities: map-merge(
    $utilities,
    (
        "white-space": (
            property: white-space,
            class: text,
            values: (
                wrap: normal,
                nowrap: nowrap,
                break-spaces: break-spaces
            )
        ),
        "viewport-height": (
            property: height,
            class: vh,
            values: (
                25: 25vh,
                50: 50vh,
                75: 75vh,
                100: 100vh
            )
        )
    )
);


///// Remainder of Bootstrap imports

@import "~bootstrap/scss/root";
@import "~bootstrap/scss/reboot";
@import "~bootstrap/scss/type";
@import "~bootstrap/scss/images";
@import "~bootstrap/scss/containers";
@import "~bootstrap/scss/grid";
@import "~bootstrap/scss/tables";
@import "~bootstrap/scss/forms";
@import "~bootstrap/scss/buttons";
@import "~bootstrap/scss/transitions";
@import "~bootstrap/scss/dropdown";
@import "~bootstrap/scss/button-group";
@import "~bootstrap/scss/nav";
@import "~bootstrap/scss/navbar";
@import "~bootstrap/scss/card";
@import "~bootstrap/scss/accordion";
@import "~bootstrap/scss/breadcrumb";
@import "~bootstrap/scss/pagination";
@import "~bootstrap/scss/badge";
@import "~bootstrap/scss/alert";
@import "~bootstrap/scss/progress";
@import "~bootstrap/scss/list-group";
@import "~bootstrap/scss/close";
@import "~bootstrap/scss/toasts";
@import "~bootstrap/scss/modal";
@import "~bootstrap/scss/tooltip";
@import "~bootstrap/scss/popover";
@import "~bootstrap/scss/carousel";
@import "~bootstrap/scss/spinners";
@import "~bootstrap/scss/offcanvas";
@import "~bootstrap/scss/placeholders";
@import "~bootstrap/scss/helpers";
@import "~bootstrap/scss/utilities/api";

Schriften

Verwendet werden die Schriften Roboto (400, 500, 700) und Roboto Condensed (400, 500).

Die benötigten Dateien befinden sich in /resources/fonts. Die Definitionen finden sich in /components/site/fonts.scss und werden in diesem Setup über /components/site/site.scss eingebunden.

Mehrfachauswahl

Momentan wird TomSelect verwendet. Anforderung ist, dass die Darstellung im Standard einzeilig bleibt, auch wenn sehr viele Optionen ausgewählt worden sind. Siehe auch das Beispiel bei den Formularen.

Folgend der Setup-Code:

import 'tom-select/dist/js/tom-select.complete.min.js';
import TomSelect from 'tom-select';

const tomSelectSettings = {
    onInitialize: function() {
        this.dropdownWatch = null;
        this.dropdownWatchCb = () => {
            if (this.isOpen) {
                const controlRect = this.control.getBoundingClientRect();
                const dropdownRect = this.dropdown.getBoundingClientRect();
                const spaceAbove = controlRect.top;
                const spaceBelow = window.innerHeight - controlRect.bottom;

                if (spaceBelow < dropdownRect.height && spaceAbove > spaceBelow) {
                    this.wrapper.classList.add('dropdown-top');
                } else {
                    this.wrapper.classList.remove('dropdown-top');
                }
            }
        }
    },
    onDropdownOpen: function() {
        this.dropdownWatchCb();
        this.dropdownWatch = setInterval(this.dropdownWatchCb, 250);
    },
    onDropdownClose: function() {
        clearInterval(this.dropdownWatch)
    },
    plugins: {
        remove_button:{
            title: 'Auswahl entfernen',
        },
		'checkbox_options': {
			'checkedClassNames':   ['form-check-input', 'ts-checked'],
			'uncheckedClassNames': ['form-check-input', 'ts-unchecked'],
		}
	},
};

for (const select of document.querySelectorAll('.multi-select')) {
    const tomSelect = new TomSelect(select, tomSelectSettings);

    // Make sure the dropdown triggers when just clicking inside the control.
    // Our "one-liner select" CSS actually hides the real trigger input from
    // TomSelect and remedy that a bit here.
    tomSelect.on('focus', event => {
        tomSelect.refreshOptions();
        tomSelect.open();
    });
}

Icons

Wir verwenden die Outline-Variante der Tabler Icons. Eine Integration mit Vue ist verfügbar.

Diagramme

Es wird Apache ECharts verwendet. Hier scheint es ebenfalls eine aktuelle Vue-Integration zu geben.

Es wird GLightbox verwendet. Die Bibliothek kann über npm eingebunden werden. Wir verwenden als Selektor [data-toggle="lightbox"].

Ein Beispiel findet sich hier.

import GLightbox from 'glightbox';
import 'glightbox/dist/css/glightbox.min.css';

const lightbox= GLightbox({
    selector: '[data-toggle="lightbox"]'
});