Replace styleguide components

This commit is contained in:
Maximilian Harz 2025-06-24 13:50:19 +02:00
parent 25942d17b7
commit 636fda45f2
18 changed files with 642 additions and 510 deletions

View File

@ -1,20 +1,73 @@
<script setup lang="ts">
import Maintenance from './components/maintenance.vue';
<template>
<transition name="fade" appear>
<div class="container">
<base-card>
<div class="flex-container">
<div class="item">
<a
:href="emails.ORGANIZATION_LINK"
:title="$t('login.moreInfo', metadata)"
target="_blank"
>
<img class="image" alt="Under maintenance" src="/img/custom/logo-squared.svg" >
</a>
</div>
<div class="item">
<h3>{{ $t('maintenance.title', metadata) }}</h3>
</div>
<div class="explanation">
<p>{{ $t('maintenance.explanation') }}</p>
<p>
{{ $t('maintenance.questions') }}
<a :href="'mailto:' + supportEmail">{{ supportEmail }}</a>
</p>
</div>
</div>
</base-card>
</div>
</transition>
</template>
<script setup>
import emails from '~/constants/emails'
import metadata from '~/constants/metadata'
import BaseCard from '~/components/BaseCard/BaseCard.vue'
const supportEmail = emails.supportEmail
</script>
<template>
<div>
<Maintenance />
</div>
</template>
<style src="./style.css" lang="scss" />
<style>
:root {
--space-base: 16px;
--space-small: 8px;
--space-x-small: 6px;
--space-xx-small: 4px;
--color-locale-menu: #333;
}
<style scoped lang="scss">
@use './media-queries.scss' as media-queries;
.image {
width: 75%;
height: auto;
}
.flex-container {
display: flex;
gap: var(--space-small);
}
.item {
margin-top: var(--space-small);
}
.container {
padding: 0 var(--space-small);
margin: 0 auto;
max-width: var(--md);
@media #{media-queries.$medium} {
padding: 0 var(--space-base);
}
@media #{media-queries.$large} {
padding: 0 var(--space-large);
}
}
</style>

View File

@ -0,0 +1,79 @@
import { storiesOf } from '@storybook/vue'
import helpers from '~/storybook/helpers'
import logos from '~/constants/logos.js'
import BaseCard from './BaseCard.vue'
storiesOf('Generic/BaseCard', module)
.addDecorator(helpers.layout)
.add('default', () => ({
components: { BaseCard },
template: `
<base-card>
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,
}))
.add('with slot: hero image', () => ({
components: { BaseCard },
template: `
<base-card style="width: 400px;">
<template #heroImage>
<img class="image" src="https://unsplash.com/photos/R4y_E5ZQDPg/download" />
</template>
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,
}))
.add('with slot: image column', () => ({
components: { BaseCard },
template: `
<base-card style="width: 600px;">
<template #imageColumn>
<img class="image" alt="Example image" src="${logos.LOGO_WELCOME_PATH}" />
</template>
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,
}))
.add('with slot: topMenu', () => ({
components: { BaseCard },
template: `
<base-card style="width: 600px;">
<template #imageColumn>
<img class="image" alt="Example image" src="${logos.LOGO_WELCOME_PATH}" />
</template>
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
<template #topMenu>
<base-button size="small">Menu</base-button>
</template>
</base-card>
`,
}))
.add('with highlight prop', () => ({
components: { BaseCard },
template: `
<base-card highlight style="width: 400px;">
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,
}))
.add('with wideContent prop', () => ({
components: { BaseCard },
template: `
<base-card wideContent style="width: 400px;">
<h2 class="title">I am a card heading</h2>
<p>And I am a paragraph.</p>
</base-card>
`,
}))

View File

@ -0,0 +1,133 @@
<template>
<article :class="classNames">
<template v-if="$slots.imageColumn">
<aside class="image-column">
<slot name="imageColumn" />
</aside>
<section class="content-column">
<slot />
</section>
</template>
<template v-else-if="$slots.heroImage">
<section class="hero-image">
<slot name="heroImage" />
</section>
<slot />
</template>
<slot v-else />
<aside v-if="$slots.topMenu" class="top-menu">
<slot name="topMenu" />
</aside>
</article>
</template>
<script>
export default {
props: {
highlight: {
type: Boolean,
default: false,
},
wideContent: {
type: Boolean,
default: false,
},
},
computed: {
classNames() {
let classNames = 'base-card'
if (this.$slots.imageColumn) classNames += ' --columns'
if (this.highlight) classNames += ' --highlight'
if (this.wideContent) classNames += ' --wide-content'
return classNames
},
},
}
</script>
<style>
.base-card {
position: relative;
padding: var(--space-base);
border-radius: var(--border-radius-x-large);
overflow: hidden;
overflow-wrap: break-word;
background-color: var(--color-neutral-100);
box-shadow: var(--box-shadow-base);
&.--columns {
display: flex;
}
&.--highlight {
border: var(--border-size-base) solid var(--color-warning);
}
&.--wide-content {
padding: var(--space-small)
> .hero-image {
width: calc(100% + (2 * var(--space-small)));
margin: calc(-1 * var(--space-small));
margin-bottom: var(--space-small);
}
}
> .title,
> .content-column > .title {
font-size: var(--font-size-large);
margin-bottom: var(--space-x-small);
}
> .hero-image {
width: calc(100% + (2 * var(--space-base)));
max-height: var(--size-image-max-height);
margin: calc(-1 * var(--space-base));
margin-bottom: var(--space-base);
overflow: hidden;
> .image {
width: 100%;
object-fit: contain;
}
}
> .image-column {
flex-basis: 50%;
display: flex;
justify-content: center;
align-items: center;
padding-right: var(--space-base);
.image {
width: 100%;
max-width: 200px;
}
}
> .content-column {
flex-basis: 50%;
}
> .top-menu {
position: absolute;
top: var(--space-small);
left: var(--space-small);
}
}
@media (max-width: 565px) {
.base-card.--columns {
flex-direction: column;
> .image-column {
padding-right: 0;
margin-bottom: var(--space-base);
}
}
}
</style>

View File

@ -1,80 +0,0 @@
<template>
<transition name="fade" appear>
<ds-container width="medium">
<base-card>
<ds-space>
<!-- <locale-switch class="login-locale-switch" offset="5" /> -->
</ds-space>
<ds-flex>
<ds-flex-item :width="{ base: '100%', sm: 1, md: 1 }">
<ds-space>
<!-- QUESTION: could we have internal page or even all internal pages here as well with PageParamsLink by having the footer underneath? -->
<!-- I tried this out, but only could get the nginx page displayed. I guees the there were nuxt errors, because the nuxt config file 'webapp/maintenance/source/nuxt.config.maintenance.js' would have to be refactored for that as well and may be the missing folder `components/_new/generic/` plays a role, see https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4619 -->
<!-- <page-params-link :pageParams="links.ORGANIZATION" :title="$t('login.moreInfo', metadata)">
<logo type="maintenance" />
</page-params-link> -->
<!-- BUT: not the logo and not even the a-tag is working at the moment -->
<!-- <a
:href="emails.ORGANIZATION_LINK"
:title="$t('login.moreInfo', metadata)"
target="_blank"
> -->
<img class="image" alt="Under maintenance" src="/img/custom/logo-squared.svg" >
<!-- </a> -->
</ds-space>
</ds-flex-item>
<ds-flex-item :width="{ base: '100%', sm: 1, md: 1 }">
<ds-flex-item>
<ds-heading tag="h3">{{ $t('maintenance.title', metadata) }}</ds-heading>
</ds-flex-item>
<ds-flex-item>
<ds-space margin="small">
<ds-text>{{ $t('maintenance.explanation') }}</ds-text>
<ds-text>
{{ $t('maintenance.questions') }}
<a :href="'mailto:' + supportEmail">{{ supportEmail }}</a>
</ds-text>
</ds-space>
</ds-flex-item>
</ds-flex-item>
</ds-flex>
</base-card>
</ds-container>
</transition>
</template>
<script>
import emails from '~/constants/emails'
// import links from '~/constants/links.js'
import metadata from '~/constants/metadata'
// import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch'
// import Logo from '~/components/Logo/Logo'
import { Container as DsContainer, Card as BaseCard, Space as DsSpace, Flex as DsFlex, FlexItem as DsFlexItem, Heading as DsHeading, Text as DsText } from '../lib/styleguide'
export default {
components: {
// LocaleSwitch,
// Logo,
DsContainer,
BaseCard,
DsSpace,
DsFlex,
DsFlexItem,
DsHeading,
DsText,
},
layout: 'blank',
data() {
// return { links, metadata, supportEmail: emails.SUPPORT_EMAIL }
return { metadata, supportEmail: emails.SUPPORT_EMAIL }
},
}
</script>
<style lang="scss">
.image {
width: 75%;
height: auto;
}
</style>

View File

@ -10,15 +10,12 @@ import Radio from './data-input/Radio/Radio.vue'
import Select from './data-input/Select/Select.vue'
import Container from './layout/Container/Container.vue'
import Card from './layout/Card/Card.vue'
import Flex from './layout/Flex/Flex.vue'
import FlexItem from './layout/Flex/FlexItem.vue'
import Grid from './layout/Grid/Grid.vue'
import Modal from './layout/Modal/Modal.vue'
import Page from './layout/Page/Page.vue'
import PageTitle from './layout/PageTitle/PageTitle.vue'
import Placeholder from './layout/Placeholder/Placeholder.vue'
import Section from './layout/Section/Section.vue'
import Space from './layout/Space/Space.vue'
import Spinner from './layout/Spinner/Spinner.vue'
import Button from './navigation/Button/Button.vue'
import Menu from './navigation/Menu/Menu.vue'
@ -43,15 +40,12 @@ export {
Select,
Container,
Card,
Flex,
FlexItem,
Grid,
Modal,
Page,
PageTitle,
Placeholder,
Section,
Space,
Spinner,
Button,
Menu,

View File

@ -1,94 +0,0 @@
<template>
<component
:is="tag"
:style="styles"
class="ds-flex">
<slot />
</component>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { getSpace } from '@@/utils'
import { mediaQuery } from '@@/mixins'
/**
* Used in combination with the flex item component to create flexible layouts.
* @version 1.0.0
*/
export default defineComponent({
name: 'DsFlex',
mixins: [mediaQuery],
provide() {
return {
$parentFlex: this
}
},
props: {
/**
* Default gutter size of columns
*/
gutter: {
type: [String, Object],
default: null
},
/**
* Default width of columns
*/
width: {
type: [String, Number, Object],
default: 1
},
/**
* Direction of the flex items
* @options row|row-reverse|column|column-reverse
*/
direction: {
type: [String, Object],
default: null
},
/**
* The outtermost html tag
*/
tag: {
type: String,
default: 'div'
}
},
computed: {
styles() {
const gutter = this.mediaQuery(this.gutter)
const direction = this.mediaQuery(this.direction)
const gutterStyle = gutter ? this.parseGutter(gutter) : {}
const directionStyle = direction ? this.parseDirection(direction) : {}
return {
...gutterStyle,
...directionStyle
}
}
},
methods: {
parseGutter(gutter: string) {
const realGutter = getSpace(gutter)
return {
marginLeft: `-${realGutter / 2}px`,
marginRight: `-${realGutter / 2}px`
}
},
parseDirection(direction: string) {
return {
flexDirection: direction
}
}
},
});
</script>
<style lang="scss" src="./style.scss">
</style>

View File

@ -1,107 +0,0 @@
<template>
<component
:is="tag"
:style="styles"
class="ds-flex-item">
<slot />
</component>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { getSpace } from '@@/utils'
import { mediaQuery } from '@@/mixins'
/**
* @version 1.0.0
* @see DsFlex
*/
export default defineComponent({
name: 'DsFlexItem',
mixins: [mediaQuery],
inject: {
$parentFlex: {
default: null
}
},
props: {
/**
* The item's width
* @default 1
*/
width: {
type: [String, Number, Object],
default() {
return this.$parentFlex ? this.$parentFlex.width : 1
}
},
/**
* The outtermost html tag
*/
tag: {
type: String,
default: 'div'
},
/**
* Center content vertical and horizontal
*/
centered: {
type: Boolean,
default: false
}
},
computed: {
gutter() {
return this.$parentFlex ? this.$parentFlex.gutter : 0
},
styles() {
const width = this.mediaQuery(this.width)
const gutter = this.mediaQuery(this.gutter)
const widthStyle = this.parseWidth(width)
const gutterStyle = this.parseGutter(gutter)
const centerStyle = this.centered
? {
'align-self': 'center',
'jusify-self': 'center'
}
: {}
return {
...widthStyle,
...gutterStyle,
...centerStyle
}
}
},
methods: {
parseWidth(width: string | number) {
const styles = {}
if (isNaN(width)) {
styles.flexBasis = width
styles.width = width
} else {
styles.flexGrow = width
styles.flexShrink = 0
styles.flexBasis = 0
}
return styles
},
parseGutter(gutter: string) {
const realGutter = getSpace(gutter)
if (realGutter === 0) {
return {}
}
return {
paddingLeft: `${realGutter / 2}px`,
paddingRight: `${realGutter / 2}px`,
marginBottom: `${realGutter}px`
}
}
},
});
</script>

View File

@ -1,12 +0,0 @@
@use "@@/styles/shared" as *;
.ds-flex {
@include reset;
display: flex;
flex-wrap: wrap;
}
.ds-flex-item {
@include reset;
@include layout-flex-fix;
}

View File

@ -1,119 +0,0 @@
<template>
<component
:is="tag"
:style="styles"
class="ds-space"
:class="[
centered && 'ds-space-centered'
]"
>
<slot />
</component>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { getSpace } from '@@/utils'
import { mediaQuery } from '@@/mixins'
/**
* Use this component for grouping and separation.
* @version 1.0.0
*/
export default defineComponent({
name: 'DsSpace',
mixins: [mediaQuery],
inject: {
$parentRow: {
default: null
}
},
props: {
/**
* The top margin of this space.
*/
marginTop: {
type: [String, Object],
default: null
},
/**
* The bottom margin of this space.
*/
marginBottom: {
type: [String, Object],
default: 'large'
},
/**
* The bottom and top margin of this space.
*/
margin: {
type: [String, Object],
default: null
},
/**
* Center content vertacally and horizontally
*/
centered: {
type: Boolean,
default: false
},
/**
* The html element name used for this space.
*/
tag: {
type: String,
default: 'div'
}
},
computed: {
styles() {
const top = this.margin ? this.margin : this.marginTop
const bottom = this.margin ? this.margin : this.marginBottom
const marginTop = this.mediaQuery(top)
const marginBottom = this.mediaQuery(bottom)
const marginTopStyle = this.parseMargin('Top')(marginTop)
const marginBottomStyle = this.parseMargin('Bottom')(marginBottom)
const centerStyle = this.centered
? {
'text-align': 'center',
flex: 1,
'align-content': 'center',
'jusify-content': 'center'
}
: {}
return {
...marginTopStyle,
...marginBottomStyle,
...centerStyle
}
}
},
methods: {
parseMargin(direction) {
return margin => {
const styles = {}
if (!margin) {
return styles
}
const realMargin = getSpace(margin)
if (realMargin !== 0) {
styles[`margin${direction}`] = `${realMargin}px`
}
return styles
}
}
},
});
</script>
<style lang="scss" src="./style.scss">
</style>

View File

@ -1,9 +0,0 @@
@use "@@/styles/shared" as *;
.ds-space {
@include reset;
}
.ds-space-centered {
text-align: center;
}

View File

@ -0,0 +1,55 @@
<template>
<component
:is="tag"
class="ds-logo"
:class="[inverse && 'ds-logo-inverse']"
>
<svg-logo
v-if="!inverse"
class="ds-logo-svg"
/>
<svg-logo-inverse
v-else
class="ds-logo-svg"
/>
</component>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import svgLogo from '@@/assets/img/Logo-Horizontal.svg'
import svgLogoInverse from '@@/assets/img/Logo-Horizontal-Dark.svg'
/**
* This component displays the brand's logo.
* @version 1.0.0
*/
export default defineComponent({
name: 'DsLogo',
components: {
svgLogo,
svgLogoInverse
},
props: {
/**
* Inverse the logo
*/
inverse: {
type: Boolean,
default: false
},
/**
* The html element name used for the logo.
*/
tag: {
type: String,
default: 'div'
}
},
});
</script>
<style lang="scss" src="./style.scss">
</style>

View File

@ -0,0 +1,20 @@
@use "@@/styles/shared" as *;
@use "@@/styles/tokens/tokens" as *;
.ds-logo {
@include reset;
display: inline-flex;
justify-content: center;
align-items: center;
color: $text-color-primary;
}
.ds-logo-inverse {
color: $text-color-primary-inverse;
}
.ds-logo-svg {
width: 130px;
height: auto;
fill: currentColor;
}

View File

@ -1,9 +1,8 @@
import '@@/styles/main.scss'
import * as utils from './utils'
import * as mixins from './mixins'
export * from './components'
export { utils, mixins }
export { utils }

View File

@ -1,3 +0,0 @@
import mediaQuery from './media-query'
export { mediaQuery }

View File

@ -1,62 +0,0 @@
import { tokenMap } from '@@/styles/tokens'
const windowSize = {
width: null,
height: null,
}
function updateWindowSize() {
windowSize.width =
document.documentElement.clientWidth || document.body.clientWidth
windowSize.height =
document.documentElement.clientHeight || document.body.clientHeight
}
let init = false
function initListener() {
if (init) {
return
}
try {
if (window && typeof window !== 'undefined') {
window.addEventListener('resize', updateWindowSize)
updateWindowSize()
}
init = true
} catch (err) {
init = true
return false
}
}
/**
* @mixin
*/
export default {
data() {
return {
mediaQueryWindowSize: windowSize,
}
},
methods: {
mediaQuery(arg) {
initListener()
if (arg === null || typeof arg !== 'object') {
return arg
}
let result = arg.base
Object.keys(tokenMap.mediaSize)
.reverse()
.some((key) => {
const width = tokenMap.mediaSize[key].value
if (width <= this.mediaQueryWindowSize.width && arg[key]) {
result = arg[key]
return true
}
})
return result
},
},
}

View File

@ -0,0 +1,5 @@
$x-small: '(screen and (min-width: 480px))';
$small: '(screen and (min-width: 600px))';
$medium: '(screen and (min-width: 768px))';
$large: '(screen and (min-width: 1024px))';
$x-large: '(screen and (min-width: 1200px))';

View File

@ -30,7 +30,7 @@ export default defineNuxtConfig({
plugins: [viteTsConfigPaths()],
resolve: {
alias: {
'@@': resolve(__dirname, './src'),
'@@': resolve(__dirname, './lib/styleguide'),
}
},
build: {

280
maintenance/style.css Normal file
View File

@ -0,0 +1,280 @@
:root {
/* Color Brand */
--color-primary: rgb(23, 181, 63);
--color-primary-light: rgb(96, 214, 98);
--color-primary-dark: rgb(25, 122, 49);
--color-primary-active: rgb(25, 194, 67);
--color-primary-inverse: rgb(241, 253, 244);
--color-secondary: rgb(0, 142, 230);
--color-secondary-active: rgb(10, 161, 255);
--color-secondary-inverse: rgb(240, 249, 255);
--color-success: rgb(23, 181, 63);
--color-success-active: rgb(26, 203, 71);
--color-success-inverse: rgb(241, 253, 244);
--color-danger: rgb(219, 57, 36);
--color-danger-light: rgb(242, 97, 65);
--color-danger-dark: rgb(158, 43, 28);
--color-danger-active: rgb(224, 81, 62);
--color-danger-inverse: rgb(253, 243, 242);
--color-warning: rgb(230, 121, 25);
--color-warning-active: rgb(233, 137, 53);
--color-warning-inverse: rgb(253, 247, 241);
--color-yellow: rgb(245, 196, 0);
--color-yellow-active: rgb(255, 206, 10);
--color-yellow-inverse: rgb(255, 252, 240);
/* Color Neutral */
--color-neutral-0: rgb(25, 23, 28);
--color-neutral-10: rgb(40, 37, 45);
--color-neutral-20: rgb(75, 69, 84);
--color-neutral-30: rgb(100, 92, 112);
--color-neutral-40: rgb(112, 103, 126);
--color-neutral-50: rgb(151, 143, 163);
--color-neutral-60: rgb(177, 171, 186);
--color-neutral-70: rgb(203, 199, 209);
--color-neutral-80: rgb(229, 227, 232);
--color-neutral-85: rgb(239, 238, 241);
--color-neutral-90: rgb(245, 244, 246);
--color-neutral-95: rgb(250, 249, 250);
--color-neutral-100: rgb(255, 255, 255);
/* Color Text */
--text-color-base: rgb(75, 69, 84);
--text-color-soft: rgb(112, 103, 126);
--text-color-softer: rgb(177, 171, 186);
--text-color-disabled: rgb(177, 171, 186);
--text-color-inverse: rgb(250, 249, 250);
--text-color-link: rgb(23, 181, 63);
--text-color-link-active: rgb(25, 194, 67);
--text-color-primary: rgb(23, 181, 63);
--text-color-primary-inverse: rgb(241, 253, 244);
--text-color-secondary: rgb(0, 142, 230);
--text-color-secondary-inverse: rgb(240, 249, 255);
--text-color-success: rgb(23, 181, 63);
--text-color-success-inverse: rgb(241, 253, 244);
--text-color-warning: rgb(230, 121, 25);
--text-color-warning-inverse: rgb(253, 247, 241);
--text-color-danger: rgb(219, 57, 36);
--text-color-danger-inverse: rgb(253, 243, 242);
/* Color Background */
--background-color-base: rgb(255, 255, 255);
--background-color-soft: rgb(250, 249, 250);
--background-color-softer: rgb(245, 244, 246);
--background-color-softer-active: rgb(250, 249, 250);
--background-color-softest: rgb(239, 238, 241);
--background-color-softest-active: rgb(245, 244, 246);
--background-color-inverse: rgb(25, 23, 28);
--background-color-inverse-soft: rgb(40, 37, 45);
--background-color-inverse-softer: rgb(75, 69, 84);
--background-color-inverse-softer-active: rgb(100, 92, 112);
--background-color-disabled: rgb(245, 244, 246);
--background-color-primary: rgb(23, 181, 63);
--background-color-primary-active: rgb(25, 194, 67);
--background-color-primary-inverse: rgb(241, 253, 244);
--background-color-secondary: rgb(0, 142, 230);
--background-color-secondary-active: rgb(10, 161, 255);
--background-color-secondary-inverse: rgb(240, 249, 255);
--background-color-third: rgb(126, 82, 204);
--background-color-third-active: rgb(160, 103, 255);
--background-color-third-inverse: rgb(239, 230, 255);
--background-color-success: rgb(23, 181, 63);
--background-color-success-active: rgb(26, 203, 71);
--background-color-success-inverse: rgb(241, 253, 244);
--background-color-warning: rgb(230, 121, 25);
--background-color-warning-active: rgb(233, 137, 53);
--background-color-warning-inverse: rgb(253, 247, 241);
--background-color-danger: rgb(219, 57, 36);
--background-color-danger-active: rgb(224, 81, 62);
--background-color-danger-inverse: rgb(253, 243, 242);
/* Color Border */
--border-color-base: rgb(177, 171, 186);
--border-color-soft: rgb(203, 199, 209);
--border-color-softer: rgb(229, 227, 232);
--border-color-softest: rgb(245, 244, 246);
--border-color-active: rgb(23, 181, 63);
--border-color-primary: rgb(23, 181, 63);
--border-color-success: rgb(23, 181, 63);
--border-color-warning: rgb(230, 121, 25);
--border-color-danger: rgb(219, 57, 36);
/* Border Size */
--border-size-base: 1px;
--border-size-large: 3px;
--border-size-x-large: 6px;
/* Border Radius */
--border-radius-x-large: 5px;
--border-radius-large: 4px;
--border-radius-base: 4px;
--border-radius-small: 2px;
--border-radius-rounded: 2em;
--border-radius-circle: 50%;
/* Font Size */
--font-size-xxxx-large: 3rem;
--font-size-xxx-large: 2.5rem;
--font-size-xx-large: 2rem;
--font-size-x-large: 1.5rem;
--font-size-large: 1.25rem;
--font-size-base: 1rem;
--font-size-body: 15px;
--font-size-small: 0.8rem;
--font-size-x-small: 0.7rem;
--font-size-xx-small: 0.6rem;
/* Font Space */
--font-space-xxxx-large: 2em;
--font-space-xxx-large: 1.5em;
--font-space-xx-large: 1.2em;
--font-space-x-large: 1em;
--font-space-large: 0.6em;
--font-space-base: 0.5em;
--font-space-small: 0.4em;
--font-space-x-small: 0.3em;
--font-space-xx-small: 0.2em;
--font-space-xxx-small: 0.1em;
/* Font Family */
--font-family-heading: 'LatoWeb', sans-serif;
--font-family-text: 'LatoWeb', sans-serif;
--font-family-serif: 'Gentium Basic', serif;
--font-family-code: inconsolata, monospace;
/* Font Weight */
--font-weight-regular: normal;
--font-weight-bold: 600;
/* Line Height */
--line-height-large: 1.5rem;
--line-height-base: 1.3rem;
--line-height-small: 1.1rem;
--line-height-smaller: 1.0rem;
/* Letter Spacing */
--letter-spacing-x-large: 0.1em;
--letter-spacing-large: 0.05em;
--letter-spacing-base: 0;
--letter-spacing-small: -0.01em;
--letter-spacing-x-small: -0.015em;
/* Opacity */
--opacity-base: 1;
--opacity-soft: 0.7;
--opacity-disabled: 0.5;
/* Space */
--space-xxx-large: 128px;
--space-xx-large: 64px;
--space-x-large: 48px;
--space-large: 32px;
--space-base: 24px;
--space-small: 16px;
--space-x-small: 8px;
--space-xx-small: 4px;
--space-xxx-small: 2px;
--space-none: 0;
/* Size Height */
--size-height-base: 42px;
--size-height-large: 50px;
--size-height-xlarge: 60px;
--size-height-footer: 64px;
--size-height-connections: 315px;
--size-tappable-square: 44px;
--size-ribbon: 6px;
/* Size Width */
--size-width-filter-sidebar: 85px;
--size-width-paginate: 200px;
--size-max-width-filter-menu: 1026px;
/* Size Avatar */
--size-avatar-small: 34px;
--size-avatar-base: 44px;
--size-avatar-large: 114px;
/* Size Buttons */
--size-button-large: 50px;
--size-button-base: 36px;
--size-button-small: 26px;
/* Size Images */
--size-image-max-height: 2000px;
--size-image-cropper-max-height: 600px;
--size-image-cropper-min-height: 400px;
--size-image-uploader-min-height: 250px;
/* Size Icons */
--size-icon-base: 16px;
--size-icon-large: 60px;
/* Shadow */
--box-shadow-x-large: 0 15px 30px 0 rgba(0,0,0,.11),0 5px 15px 0 rgba(0,0,0,.08);
--box-shadow-large: 0 10px 20px 0 rgba(0,0,0,.11),0 3px 10px 0 rgba(0,0,0,.08);
--box-shadow-base: 0px 12px 26px -4px rgba(0, 0, 0, .1);
--box-shadow-small: 0px 8px 18px -2px rgba(0, 0, 0, .1);
--box-shadow-x-small: 0px 0px 3px 0px rgba(0, 0, 0, .1);
--box-shadow-active: 0 0 6px 1px rgba(20, 100, 160, 0.5);
--box-shadow-inset: inset 0 0 20px 1px rgba(0,0,0,.15);
--box-shadow-small-inset: inset 0 0 0 1px rgba(0,0,0,.05);
/* Effects */
--blur-radius: 22px;
/* Animation Duration */
--duration-short: 0.08s;
--duration-base: 0.5s;
--duration-long: 0.75s;
--duration-x-long: 1s;
--duration-xx-long: 2s;
/* Animation Ease */
--ease-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--ease-out-sharp: cubic-bezier(0.165, 0.84, 0.44, 1);
--ease-out-bounce: cubic-bezier(.87,-.41,.19,1.44);
--ease-in: cubic-bezier(0.55, 0.085, 0.68, 0.53);
--ease-in-sharp: cubic-bezier(0.895, 0.03, 0.685, 0.22);
/* Z-Index */
--z-index-modal: 9999;
--z-index-overlay: 9000;
--z-index-dropdown: 8888;
--z-index-page-submenu: 2500;
--z-index-page-header: 2000;
--z-index-page-sidebar: 1500;
--z-index-sticky-float: 150;
--z-index-sticky: 100;
--z-index-post-teaser-link: 5;
--z-index-surface: 1;
/* Custom Tokens (Footer, Header, etc.) */
--color-header-background: var(--color-neutral-100);
--color-footer-background: var(--color-neutral-100);
--color-footer-link: var(--color-primary);
--color-locale-menu: var(--text-color-soft);
--color-donation-bar: var(--color-primary);
--color-donation-bar-light: var(--color-primary-light);
--color-toast-red: var(--color-danger);
--color-toast-orange: var(--color-warning);
--color-toast-yellow: var(--color-yellow);
--color-toast-blue: var(--color-secondary);
--color-toast-green: var(--color-success);
--color-ribbon-event: var(--background-color-third);
--color-ribbon-event-active: var(--background-color-third-active);
--color-ribbon-article: var(--background-color-secondary);
--color-ribbon-article-active: var(--background-color-secondary-active);
/* Chat Tokens */
--chat-message-bg-me: var(--color-primary-light);
--chat-message-color: var(--text-color-base);
--chat-message-bg-others: var(--color-neutral-80);
--chat-sidemenu-bg: var(--color-secondary-active);
--chat-new-message-color: var(--color-secondary-active);
--chat-message-timestamp: var(--text-color-soft);
--chat-message-checkmark-seen: var(--text-color-secondary);
--chat-message-checkmark: var(--text-color-soft);
--chat-room-color-counter-badge: var(--text-color-inverse);
--chat-room-background-counter-badge: var(--color-secondary);
}