app/assets/stylesheets/application.scss
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any styles
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
* file per style scope.
*
*= require_tree .
*= require_self
*/
// Bulma variable overrides
$primary: #ff7f00;
$radius-small: 8px;
$radius: 8px;
$card-radius: 16px;
$card-background-color: #fff;
$link: $primary;
$modal-card-head-background-color: white;
$modal-card-head-border-bottom: 2px solid whitesmoke;
$modal-card-foot-border-top: 2px solid whitesmoke;
// Custom variables
$logo-height: 155px;
// Include Bulma SCSS
@import "bulma";
// Incluse Font Awesome SCSS
@import "font-awesome-sprockets";
@import "font-awesome";
html {
max-height: 100vh;
min-height: 100vh;
overflow-y: auto;
}
body {
background-color: $white-bis;
min-height: 100vh;
max-height: 100vh;
overflow-y: auto;
overflow-x: hidden;
display: flex;
flex-direction: column;
* {
// Prevent blue highlights on elements on mobile
-webkit-tap-highlight-color: transparent;
}
::selection,
::-moz-selection {
background: $primary;
color: $white;
}
}
.container {
width: 100%;
&--main {
margin-top: 3rem;
margin-bottom: 3rem;
@include until($desktop) {
padding-left: 0.75rem;
padding-right: 0.75rem;
}
}
}
.card {
background-color: $card-background-color !important;
}
.back {
margin-bottom: 0.5rem;
&::before {
content: "\f104";
font-family: "Font Awesome 5 Free";
font-weight: 900;
color: $primary;
margin-right: 0.2rem;
}
}
// Add support for Petite Vue "v-cloak" attribute.
[v-cloak] {
display: none;
}
.landing {
&-hero {
background-image: asset-url("landing/background.svg");
background-color: $white-ter;
background-repeat: no-repeat;
background-position: right top;
background-size: 60%;
clip-path: ellipse(180% 100% at 50% 0%);
@include until($tablet) {
//background-image: none;
background-position: center bottom;
background-size: 100%;
background-image: asset-url("landing/background-tablet.svg");
}
@include until($desktop) {
background-size: 80%;
}
&-content {
height: calc(max(90vh, 500px));
@include until($tablet) {
height: auto;
}
}
&-text {
padding-top: 15vh;
@include until($tablet) {
padding-top: 2rem;
padding-left: 2rem;
padding-right: 2rem;
text-align: center;
}
@include until($desktop) {
margin-left: 0.75rem;
}
@include until($tablet) {
margin-left: 0;
}
}
&-title {
@extend .title;
@extend .is-1;
color: $primary;
}
&-description {
@extend .has-text-grey;
}
&-actions {
margin-top: 1.7rem;
}
&-register {
display: block;
color: $grey;
font-size: $size-7;
margin-top: 0.5rem;
&:hover {
cursor: pointer;
text-decoration: underline;
}
}
&-image {
min-width: 80%;
}
&-flash {
margin-bottom: 2rem;
}
}
}
.footer {
background-color: $white-ter;
clip-path: ellipse(150% 100% at 50% 100%);
margin-top: auto;
padding: 2rem 1.5rem;
a {
color: $text;
&:hover {
color: $primary;
text-decoration: underline;
}
}
@include until($tablet) {
text-align: center;
}
&-link {
padding: 0 0.5rem;
}
}
.navbar {
&.is-transparent {
background: transparent;
}
&.is-raised {
box-shadow: $card-shadow;
}
&-title {
@extend .navbar-item;
@extend .title;
@extend .is-size-5;
margin-bottom: 0 !important;
}
&-burger {
@include until($desktop) {
display: flex;
justify-content: center;
align-items: center;
}
i {
font-size: 1.5rem;
}
}
}
.sidebar {
display: flex;
flex-direction: column;
width: 100%;
top: 0;
@include from($tablet) {
padding-right: 1rem;
}
// Make all children grow the entire width of the sidebar
> * {
width: 100%;
}
// Spacing between items
> * + * {
margin-top: 1.5em;
}
&-user {
display: flex;
flex-direction: column;
align-items: center;
// Spacing between items
> * + * {
margin-top: 0.5em;
}
}
&-avatar {
height: 128px;
width: 128px;
border-radius: 50%;
border: 1px solid $grey-lighter;
background-color: $background;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: contain;
vertical-align: middle;
}
}
&-order {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
// Make all children grow the entire width
> * {
width: 100%;
}
// Spacing between items
> * + * {
margin-top: 0.5em;
}
}
&-settings {
// Make all children grow the entire width
> * {
width: 100%;
}
}
}
.stat-card {
@extend .card;
@extend .card-content;
display: flex;
justify-content: space-between;
align-items: center;
height: 100%;
&-icon {
border-radius: $radius;
background-color: $primary;
min-width: 70px;
min-height: 70px;
max-width: 70px;
max-height: 70px;
margin-right: 3rem;
display: flex;
justify-content: center;
align-items: center;
i {
color: white;
font-size: 1.5rem;
}
// Make icon smaller on smaller screens
@include until($desktop) {
min-width: 50px;
min-height: 50px;
max-width: 50px;
max-height: 50px;
}
}
&-text {
text-align: end;
}
&-title {
color: $grey;
}
&-value {
font-size: 2rem;
font-weight: 400;
// Make value smaller on smaller screens
@include until($desktop) {
font-size: 1.4rem;
}
}
&-hint {
a {
font-size: $size-7 !important;
color: $grey !important;
&:hover {
text-decoration: underline;
}
}
}
}
// Class for making the statistics card smaller on tablet
.is-small-mobile {
@include until($tablet) {
.stat-card {
flex-direction: column;
justify-content: center;
&-icon {
margin-right: 0;
margin-bottom: 1rem;
}
&-text,
&-value {
text-align: center;
}
}
}
}
.button {
&-flex {
height: auto;
display: flex;
flex-direction: column;
}
}
.order {
&-item {
// Border between entries
&:not(:last-child) {
border-bottom: 2px solid whitesmoke;
}
// Spacing between entries
> * {
padding-top: 1rem;
padding-bottom: 1rem;
padding-right: 1rem;
}
&-info {
width: 50%;
&-date {
color: $grey;
font-size: $size-7;
}
}
&-price {
width: 20%;
vertical-align: middle;
}
&-actions {
width: 30%;
}
}
&-new {
&-actions {
display: flex;
flex-direction: column;
align-items: center;
&-title {
color: $grey;
font-size: $size-2;
border-bottom: 2px solid whitesmoke;
padding-bottom: 0.5rem;
margin-bottom: 2rem;
// Make font and margin smaller on mobile
@include until($tablet) {
font-size: $size-4;
margin-bottom: 0.5rem;
}
}
&-or {
font-size: $size-3;
}
// Spacing between items
> * + * {
margin-top: 1rem;
}
}
&-bill {
@extend .card;
@extend .card-content;
// Spacing between items
> * + * {
margin-top: 2rem;
}
&-logo {
display: flex;
justify-content: center;
height: $logo-height;
}
&-title {
font-size: $size-4;
margin-top: 0;
}
&-overview {
&-total {
text-align: right;
}
}
&-placeholder {
margin-top: 1rem;
text-align: center;
}
&-items {
width: 100%;
th,
td {
padding-top: 0.5rem;
padding-bottom: 0.5rem;
vertical-align: middle;
&:not(:last-child) {
padding-right: 1rem;
}
}
}
&-input {
width: 70px;
}
&-loading {
position: relative;
&-spinner {
@extend .loader;
margin: auto;
// Overrides
animation: spinAround 0.8s infinite linear;
border-color: transparent transparent $primary $primary !important;
border-width: 4px !important;
width: 70px !important;
height: 70px !important;
}
}
}
}
&-overview {
&-guest {
text-align: center;
margin-bottom: 2rem;
}
&-recent {
display: flex;
flex-direction: column;
align-items: center;
position: sticky;
top: 10px;
&-avatars {
display: flex;
flex-direction: column;
// Spacing between items
> * + * {
margin-top: 0.7rem;
}
}
&-avatar {
border-radius: 50%;
height: 40px;
width: 40px;
border: 1px solid $grey-lighter;
background-color: $white;
overflow: hidden;
}
}
}
}
.user {
display: flex;
flex-direction: column;
align-items: center;
height: 100%;
flex-grow: 0;
position: relative;
&-avatar {
position: relative;
height: 150px;
width: 150px;
background-color: $background;
border-radius: 50%;
border: 1px solid $grey-lighter;
overflow: hidden;
img {
width: 100%;
height: 100%;
object-fit: contain;
vertical-align: middle;
}
&--loading {
img {
filter: blur(5px) !important;
}
// Disable clicking when loading
pointer-events: none;
}
}
&-name {
border-radius: $radius;
padding: $button-padding-vertical $button-padding-horizontal;
margin-top: -1.5rem;
text-align: center;
z-index: 10;
width: 100%;
}
&-dagschotel {
height: 80px;
width: 80px;
position: absolute;
top: 0;
left: 0;
background-color: $white-ter;
border-radius: 50%;
border: 1px solid $grey-lighter;
overflow: hidden;
text-align: center;
cursor: pointer;
z-index: 9;
img {
width: 100%;
height: 100%;
object-fit: contain;
vertical-align: middle;
}
&--loading {
img {
filter: blur(5px) !important;
}
// Disable clicking when loading
pointer-events: none;
}
}
&-loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 1000;
&-spinner {
@extend .loader;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
// Overrides
animation: spinAround 0.8s infinite linear;
border-color: transparent transparent $primary $primary !important;
border-width: 4px !important;
width: 50% !important;
height: 50% !important;
}
}
}
.products {
&-modal {
// Make card wider.
// This is especialy important on koelkast, for easier navigation.
// 1344px is the width of the ".container"
.modal-card {
min-width: calc(min(80%, 1344px));
min-height: calc(100vh - 40px);
}
// Hide the order products frame on Koelkast when the
// modal is not a target.
// This is to prevent breaking lazy-loading
&:not(:target) {
#orderProducts {
display: none;
}
}
&:target {
#orderProducts {
display: block;
}
}
// Loading spinner
&-loading {
@extend .loader;
animation: spinAround 0.8s infinite linear;
border-color: transparent transparent $primary $primary !important;
border-width: 4px !important;
width: 150px !important;
height: 150px !important;
margin: 10vh auto;
}
}
&-item {
@extend .button;
@extend .button-flex;
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
height: 100%;
width: 100%;
// Compact mixin
@mixin compact {
flex-direction: row;
}
// More compact view on mobile
&.is-compact-mobile {
@include until($tablet) {
@include compact;
}
}
// More compact view in general
&.is-compact {
@include compact;
}
&--not-clickable {
&:hover {
cursor: inherit;
border: 1px solid $button-border-color;
}
}
&-image {
width: 80px;
height: 80px;
min-width: 80px;
min-height: 80px;
overflow: hidden;
// Compact mixin
@mixin compact {
width: 40px;
height: 40px;
min-width: 40px;
min-height: 40px;
}
// More compact view on mobile
.is-compact-mobile & {
@include until($tablet) {
@include compact;
}
}
// More compact view in general
.is-compact & {
@include compact;
}
img {
width: 100%;
height: 100%;
object-fit: contain;
vertical-align: middle;
}
}
&-info {
text-align: center;
flex-grow: 1;
// Prevent overflow
max-width: 100%;
word-wrap: break-word;
white-space: initial;
// Compact mixin
@mixin compact {
text-align: left;
margin-left: 1rem;
}
// More compact view on mobile
.is-compact-mobile & {
@include until($tablet) {
@include compact;
}
}
// More compact view in general
.is-compact & {
@include compact;
}
&-name {
font-size: $size-5;
font-weight: 400;
min-width: 0;
// Compact mixin
@mixin compact {
font-size: $size-6;
}
// More compact view on mobile
.is-compact-mobile & {
@include until($tablet) {
@include compact;
}
}
// More compact view in general
.is-compact & {
@include compact;
}
&--deleted {
color: $danger;
text-decoration: line-through;
}
}
&-price {
color: $grey;
}
&-calories {
font-size: $small-font-size;
}
&-stock {
font-size: $size-7;
color: $grey;
}
&-details {
margin-top: 1rem;
}
&-deleted {
color: $danger;
font-size: $size-7;
}
}
&-actions {
margin-top: auto;
// Prevent overflow
max-width: 100%;
// Compact mixin
@mixin compact {
flex-direction: row;
align-items: center;
margin-top: 0;
}
// More compact view on mobile
.is-compact-mobile & {
@include until($tablet) {
@include compact;
}
}
// More compact view in general
.is-compact & {
@include compact;
}
// Margin for first child
> :first-child {
margin-top: 1rem;
.is-compact-mobile & {
@include until($tablet) {
margin-top: 0;
}
}
// More compact view in general
.is-compact & {
@include compact;
}
}
// Margin for last child
> :last-child {
margin-bottom: 1rem;
.is-compact-mobile & {
@include until($tablet) {
margin-bottom: 0;
}
}
// More compact view in general
.is-compact & {
@include compact;
}
}
}
}
}
.barcode {
&-items {
@extend .table--responsive;
@extend .table--responsive--labels;
width: 100%;
}
&-item {
// Border between entries
&:not(:last-child) {
border-bottom: 2px solid whitesmoke;
}
// Spacing between entries
> * {
padding-top: 1rem;
padding-bottom: 1rem;
padding-right: 1rem;
}
th,
td {
vertical-align: middle;
}
}
}
/**
* Switch
*/
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
input {
// Hide the default checkbox
opacity: 0;
width: 0;
height: 0;
&:checked + .switch-slider {
background-color: $primary !important;
&::before {
transform: translateX(26px);
}
}
}
&-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: $grey-lighter;
transition: 0.4s;
border-radius: $radius;
&::before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: $white;
transition: 0.4s;
border-radius: $radius;
}
}
}
/**
* Responsive table
*/
.table--responsive {
width: 100%;
@include until($tablet) {
&--labels {
td {
text-align: right !important;
&::before {
/*
* aria-label has no advantage, it won't be read inside a table
content: attr(aria-label);
*/
content: attr(data-label);
float: left;
font-weight: 400;
text-transform: uppercase;
}
}
}
thead {
display: none;
}
tr {
display: block;
}
td {
display: block;
padding: 0.3em 0;
width: 100%;
.table--responsive--labels {
text-align: right !important;
}
&:first-child {
padding-top: 0.6em;
}
&:last-child {
padding-bottom: 0.6em;
}
}
}
}
// Barcode Scanner Canvas
.scanner {
&-modal {
.is-active & {
display: flex;
}
justify-content: center;
align-items: center;
.modal-card-body {
background-color: black;
border-color: lighten(black, 10%);
}
}
&-canvas {
position: relative;
max-height: 100%;
max-width: 100%;
background-color: black;
overflow: hidden;
&-laser {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto 4rem;
height: 4px;
background-color: $white;
border-radius: $radius;
box-shadow: 0 0 60px 10px rgba($white, 0.6);
}
&-loading,
&-error {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
&-title {
font-size: $size-4;
color: $white;
}
&-message {
color: $grey-lighter;
}
}
canvas {
position: absolute;
left: 0;
top: 0;
max-height: 100%;
max-width: 100%;
}
}
}
/**
* Themes
*/
.theme {
// Light theme variant
&--light {
$theme-hue-rotation: var(--theme-hue-rotation, 0deg);
// Apply the requested color transformation to the application
filter: hue-rotate($theme-hue-rotation);
// Undo the filter on images or when a class is applied
// Undoing the filter is only possible by rotating back to 0deg,
// which causes a small color shift, for some reason.
img:not(.theme-blend),
.theme-no-blend {
filter: hue-rotate(calc(-1 * #{$theme-hue-rotation}));
}
}
// Dark theme variant
&--dark {
$theme-hue-rotation: var(--theme-hue-rotation, 0deg);
// Cancel out the dark theme color inversion and
// apply the requested color transformation to the application
filter: hue-rotate(calc(180deg + #{$theme-hue-rotation})) invert(1);
// Remove box shadows of items in dark mode
* {
box-shadow: none;
}
// Better contrast between background and elements
body {
background-color: darken($white, 5%);
}
// Modal background should not be white
// (white == black when using mix-blend-mode: difference)
.modal-background {
background-color: rgba(#ffffff, 0.4);
}
// Undo the filter on images or when a class is applied
// Undoing the filter is only possible by rotating back to 0deg,
// which causes a small color shift, for some reason.
img:not(.theme-blend),
.theme-no-blend {
filter: hue-rotate(calc(-1 * (180deg + #{$theme-hue-rotation}))) invert(1);
}
}
// Item for selecting a specific theme
&-item {
@extend .button;
@extend .button-flex;
display: flex;
height: 100%;
width: 100%;
padding: 1rem;
white-space: normal;
&--selected {
border-color: $grey;
&:hover {
border-color: darken($grey, 20%);
}
}
&-image {
@extend .theme-no-blend;
margin-bottom: 1rem;
height: 100px;
}
&-name {
@extend .theme-no-blend;
color: $primary;
}
&-default {
@extend .theme-no-blend;
font-size: $size-7;
}
&-selected {
@extend .theme-no-blend;
font-size: $size-7;
color: $grey;
color: darken($primary, 20%);
}
}
&-custom {
&-picker {
display: flex;
align-items: center;
// Spacing between items
> * + * {
margin-left: 1em;
}
&-input {
width: 70px;
}
&-preview {
width: 50px;
height: 50px;
border-radius: $radius;
background-color: $primary;
}
}
}
}
/**
* Overrides
*/
// Make Bulma fields with "has-addons" also fill the entire width
.field.has-addons .control:not(:last-child) {
width: 100%;
}
// Turbo progress bar color
.turbo-progress-bar {
height: 3px;
background-color: var(--turbo-progress-bar-color, $primary);
}
// Modal using :target (without JavaScript)
.modal-target {
$transition-duration: 0.2s;
display: flex;
// Transition: modal background
.modal-background {
transition: opacity $transition-duration;
}
// Transition: modal cart
.modal-card {
transition: all $transition-duration;
}
// Display the modal when the target is active.
&:target {
visibility: visible;
// Transition: modal background
.modal-background {
opacity: 1;
}
// Transition: modal card
&:target {
.modal-card {
transform: scale(1);
opacity: 1;
}
}
}
// Hide the modal when the target is not active.
&:not(:target) {
visibility: hidden;
// Transition: modal background
.modal-background {
opacity: 0;
}
// Transition: modal card
.modal-card {
transform: scale(0.7);
opacity: 0;
}
}
}
// Make the modal fullscreen on mobile/tablet
@include until($tablet) {
.modal-card-head,
.modal-card-foot {
border-radius: 0;
}
.modal-card {
min-width: 100%;
min-height: 100%;
margin: 0;
}
}
// Responsive spacing helpers
.py-1-mobile {
@include until($tablet) {
padding-top: 0.25rem;
padding-bottom: 0.25rem;
}
}
.is-fullheight {
height: 100%;
}