Added default layout to project

* Added default layout for the site

* App is in light mode by default.

* App is in light mode by default.
This commit is contained in:
Liviu Burcusel 2025-11-12 12:50:56 +01:00 committed by GitHub
parent 9a4dd4b721
commit d1967f718e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 1660 additions and 39 deletions

View file

@ -1,8 +1,8 @@
<template>
<div>
<NuxtRouteAnnouncer />
<div class="w-full min-h-screen flex justify-center items-top">
<NuxtLayout>
<NuxtPage />
</div>
</NuxtLayout>
</div>
</template>

BIN
app/assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,24 @@
html {
height: 100%;
font-size: 14px;
line-height: 1.2;
}
body {
font-family: Ubuntu, Arial, Helvetica, sans-serif;
color: var(--text-color);
background-color: var(--surface-ground);
margin: 0;
padding: 0;
min-height: 100%;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
text-decoration: none;
}
.layout-wrapper {
min-height: 100vh;
}

View file

@ -0,0 +1,8 @@
.layout-footer {
display: flex;
align-items: center;
justify-content: center;
padding: 1rem 0 1rem 0;
gap: 0.5rem;
border-top: 1px solid var(--surface-border);
}

View file

@ -0,0 +1,14 @@
.layout-main-container {
display: flex;
flex-direction: column;
min-height: 100vh;
justify-content: space-between;
padding: 6rem 2rem 0 2rem;
background-color: var(--surface-ground);
transition: margin-left var(--layout-section-transition-duration);
}
.layout-main {
flex: 1 1 auto;
padding-bottom: 2rem;
}

View file

@ -0,0 +1,160 @@
@use "mixins" as *;
.layout-sidebar {
position: fixed;
width: 20rem;
height: calc(100vh - 8rem);
z-index: 999;
overflow-y: auto;
user-select: none;
top: 6rem;
left: 2rem;
transition:
transform var(--layout-section-transition-duration),
left var(--layout-section-transition-duration);
background-color: var(--surface-overlay);
border-radius: var(--content-border-radius);
padding: 0.5rem 1.5rem;
}
.layout-menu {
margin: 0;
padding: 0;
list-style-type: none;
.layout-root-menuitem {
> .layout-menuitem-root-text {
font-size: 0.857rem;
text-transform: uppercase;
font-weight: 700;
color: var(--text-color);
margin: 0.75rem 0;
}
> a {
display: none;
}
}
a {
user-select: none;
&.active-menuitem {
> .layout-submenu-toggler {
transform: rotate(-180deg);
}
}
}
li.active-menuitem {
> a {
.layout-submenu-toggler {
transform: rotate(-180deg);
}
}
}
ul {
margin: 0;
padding: 0;
list-style-type: none;
a {
display: flex;
align-items: center;
position: relative;
outline: 0 none;
color: var(--text-color);
cursor: pointer;
padding: 0.75rem 1rem;
border-radius: var(--content-border-radius);
transition:
background-color var(--element-transition-duration),
box-shadow var(--element-transition-duration);
.layout-menuitem-icon {
margin-right: 0.5rem;
}
.layout-submenu-toggler {
font-size: 75%;
margin-left: auto;
transition: transform var(--element-transition-duration);
}
&.active-route {
font-weight: 700;
color: var(--primary-color);
}
&:hover {
background-color: var(--surface-hover);
}
&:focus {
@include focused-inset();
}
}
ul {
overflow: hidden;
border-radius: var(--content-border-radius);
li {
a {
margin-left: 1rem;
}
li {
a {
margin-left: 2rem;
}
li {
a {
margin-left: 2.5rem;
}
li {
a {
margin-left: 3rem;
}
li {
a {
margin-left: 3.5rem;
}
li {
a {
margin-left: 4rem;
}
}
}
}
}
}
}
}
}
}
.layout-submenu-enter-from,
.layout-submenu-leave-to {
max-height: 0;
}
.layout-submenu-enter-to,
.layout-submenu-leave-from {
max-height: 1000px;
}
.layout-submenu-leave-active {
overflow: hidden;
transition: max-height 0.45s cubic-bezier(0, 1, 0, 1);
}
.layout-submenu-enter-active {
overflow: hidden;
transition: max-height 1s ease-in-out;
}

View file

@ -0,0 +1,15 @@
@mixin focused() {
outline-width: var(--focus-ring-width);
outline-style: var(--focus-ring-style);
outline-color: var(--focus-ring-color);
outline-offset: var(--focus-ring-offset);
box-shadow: var(--focus-ring-shadow);
transition:
box-shadow var(--transition-duration),
outline-color var(--transition-duration);
}
@mixin focused-inset() {
outline-offset: -1px;
box-shadow: inset var(--focus-ring-shadow);
}

View file

@ -0,0 +1,48 @@
.preloader {
position: fixed;
z-index: 999999;
background: #edf1f5;
width: 100%;
height: 100%;
}
.preloader-content {
border: 0 solid transparent;
border-radius: 50%;
width: 150px;
height: 150px;
position: absolute;
top: calc(50vh - 75px);
left: calc(50vw - 75px);
}
.preloader-content:before,
.preloader-content:after {
content: "";
border: 1em solid var(--primary-color);
border-radius: 50%;
width: inherit;
height: inherit;
position: absolute;
top: 0;
left: 0;
animation: loader 2s linear infinite;
opacity: 0;
}
.preloader-content:before {
animation-delay: 0.5s;
}
@keyframes loader {
0% {
transform: scale(0);
opacity: 0;
}
50% {
opacity: 1;
}
100% {
transform: scale(1);
opacity: 0;
}
}

View file

@ -0,0 +1,110 @@
@media screen and (min-width: 1960px) {
.layout-main,
.landing-wrapper {
width: 1504px;
margin-left: auto !important;
margin-right: auto !important;
}
}
@media (min-width: 992px) {
.layout-wrapper {
&.layout-overlay {
.layout-main-container {
margin-left: 0;
padding-left: 2rem;
}
.layout-sidebar {
transform: translateX(-100%);
left: 0;
top: 0;
height: 100vh;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
border-right: 1px solid var(--surface-border);
transition:
transform 0.4s cubic-bezier(0.05, 0.74, 0.2, 0.99),
left 0.4s cubic-bezier(0.05, 0.74, 0.2, 0.99);
box-shadow:
0px 3px 5px rgba(0, 0, 0, 0.02),
0px 0px 2px rgba(0, 0, 0, 0.05),
0px 1px 4px rgba(0, 0, 0, 0.08);
}
&.layout-overlay-active {
.layout-sidebar {
transform: translateX(0);
}
}
}
&.layout-static {
.layout-main-container {
margin-left: 22rem;
}
&.layout-static-inactive {
.layout-sidebar {
transform: translateX(-100%);
left: 0;
}
.layout-main-container {
margin-left: 0;
padding-left: 2rem;
}
}
}
.layout-mask {
display: none;
}
}
}
@media (max-width: 991px) {
.blocked-scroll {
overflow: hidden;
}
.layout-wrapper {
.layout-main-container {
margin-left: 0;
padding-left: 2rem;
}
.layout-sidebar {
transform: translateX(-100%);
left: 0;
top: 0;
height: 100vh;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
transition:
transform 0.4s cubic-bezier(0.05, 0.74, 0.2, 0.99),
left 0.4s cubic-bezier(0.05, 0.74, 0.2, 0.99);
}
.layout-mask {
display: none;
position: fixed;
top: 0;
left: 0;
z-index: 998;
width: 100%;
height: 100%;
background-color: var(--maskbg);
}
&.layout-mobile-active {
.layout-sidebar {
transform: translateX(0);
}
.layout-mask {
display: block;
}
}
}
}

View file

@ -0,0 +1,201 @@
@use "mixins" as *;
.layout-topbar {
position: fixed;
height: 4rem;
z-index: 997;
left: 0;
top: 0;
width: 100%;
padding: 0 2rem;
background-color: var(--surface-card);
transition: left var(--layout-section-transition-duration);
display: flex;
align-items: center;
.layout-topbar-logo-container {
width: 20rem;
display: flex;
align-items: center;
}
.layout-topbar-logo {
display: inline-flex;
align-items: center;
font-size: 1.5rem;
border-radius: var(--content-border-radius);
color: var(--text-color);
font-weight: 500;
gap: 0.5rem;
img {
width: 3rem;
}
&:focus-visible {
@include focused();
}
}
.layout-topbar-action {
display: inline-flex;
justify-content: center;
align-items: center;
border-radius: 50%;
width: 2.5rem;
height: 2.5rem;
color: var(--text-color);
transition: background-color var(--element-transition-duration);
cursor: pointer;
&:hover {
background-color: var(--surface-hover);
}
&:focus-visible {
@include focused();
}
i {
font-size: 1.25rem;
}
span {
font-size: 1rem;
display: none;
}
&.layout-topbar-action-highlight {
background-color: var(--primary-color);
color: var(--primary-contrast-color);
}
}
.layout-menu-button {
margin-right: 0.5rem;
}
.layout-topbar-menu-button {
display: none;
}
.layout-topbar-actions {
margin-left: auto;
display: flex;
gap: 1rem;
}
.layout-topbar-menu-content {
display: flex;
gap: 1rem;
}
.layout-config-menu {
display: flex;
gap: 1rem;
}
}
@media (max-width: 991px) {
.layout-topbar {
padding: 0 2rem;
.layout-topbar-logo-container {
width: auto;
}
.layout-menu-button {
margin-left: 0;
margin-right: 0.5rem;
}
.layout-topbar-menu-button {
display: inline-flex;
}
.layout-topbar-menu {
position: absolute;
background-color: var(--surface-overlay);
transform-origin: top;
box-shadow:
0px 3px 5px rgba(0, 0, 0, 0.02),
0px 0px 2px rgba(0, 0, 0, 0.05),
0px 1px 4px rgba(0, 0, 0, 0.08);
border-radius: var(--content-border-radius);
padding: 1rem;
right: 2rem;
top: 4rem;
min-width: 15rem;
border: 1px solid var(--surface-border);
.layout-topbar-menu-content {
gap: 0.5rem;
}
.layout-topbar-action {
display: flex;
width: 100%;
height: auto;
justify-content: flex-start;
border-radius: var(--content-border-radius);
padding: 0.5rem 1rem;
i {
font-size: 1rem;
margin-right: 0.5rem;
}
span {
font-weight: medium;
display: block;
}
}
}
.layout-topbar-menu-content {
flex-direction: column;
}
}
}
.config-panel {
.config-panel-label {
font-size: 0.875rem;
color: var(--text-secondary-color);
font-weight: 600;
line-height: 1;
}
.config-panel-colors {
> div {
padding-top: 0.5rem;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
justify-content: space-between;
button {
border: none;
width: 1.25rem;
height: 1.25rem;
border-radius: 50%;
padding: 0;
cursor: pointer;
outline-color: transparent;
outline-width: 2px;
outline-style: solid;
outline-offset: 1px;
&.active-color {
outline-color: var(--primary-color);
}
}
}
}
.config-panel-settings {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
}

View file

@ -0,0 +1,68 @@
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 1.5rem 0 1rem 0;
font-family: inherit;
font-weight: 700;
line-height: 1.5;
color: var(--text-color);
&:first-child {
margin-top: 0;
}
}
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 2rem;
}
h3 {
font-size: 1.75rem;
}
h4 {
font-size: 1.5rem;
}
h5 {
font-size: 1.25rem;
}
h6 {
font-size: 1rem;
}
mark {
background: #fff8e1;
padding: 0.25rem 0.4rem;
border-radius: var(--content-border-radius);
font-family: monospace;
}
blockquote {
margin: 1rem 0;
padding: 0 2rem;
border-left: 4px solid #90a4ae;
}
hr {
border-top: solid var(--surface-border);
border-width: 1px 0 0 0;
margin: 1rem 0;
}
p {
margin: 0 0 1rem 0;
line-height: 1.5;
&:last-child {
margin-bottom: 0;
}
}

View file

@ -0,0 +1,25 @@
/* Utils */
.clearfix:after {
content: " ";
display: block;
clear: both;
}
.card {
background: var(--surface-card);
padding: 2rem;
margin-bottom: 2rem;
border-radius: var(--content-border-radius);
&:last-child {
margin-bottom: 0;
}
}
.p-toast {
&.p-toast-top-right,
&.p-toast-top-left,
&.p-toast-top-center {
top: 100px;
}
}

View file

@ -0,0 +1,11 @@
@use "./variables/_common";
@use "./_mixins";
@use "./_preloading";
@use "./_core";
@use "./_main";
@use "./_topbar";
@use "./_menu";
@use "./_footer";
@use "./_responsive";
@use "./_utils";
@use "./_typography";

View file

@ -0,0 +1,26 @@
:root {
--primary-color: var(--p-primary-color);
--primary-contrast-color: var(--p-primary-contrast-color);
--text-color: var(--p-text-color);
--text-color-secondary: var(--p-text-muted-color);
--surface-ground: var(--p-surface-100);
--surface-border: var(--p-content-border-color);
--surface-card: var(--p-content-background);
--surface-hover: var(--p-content-hover-background);
--surface-overlay: var(--p-overlay-popover-background);
--transition-duration: var(--p-transition-duration);
--maskbg: var(--p-mask-background);
--content-border-radius: var(--p-content-border-radius);
--layout-section-transition-duration: 0.2s;
--element-transition-duration: var(--p-transition-duration);
--focus-ring-width: var(--p-focus-ring-width);
--focus-ring-style: var(--p-focus-ring-style);
--focus-ring-color: var(--p-focus-ring-color);
--focus-ring-offset: var(--p-focus-ring-offset);
--focus-ring-shadow: var(--p-focus-ring-shadow);
}
:root[class="app-dark"] {
--p-text-color: #ffffff;
--surface-ground: var(--p-surface-950);
}

View file

@ -0,0 +1,3 @@
@use "primeicons/primeicons.css";
@use "~/assets/css/tailwind.css";
@use "~/assets/scss/layout/layout.scss";

21
app/layouts/Default.vue Normal file
View file

@ -0,0 +1,21 @@
<template>
<div class="layout-wrapper layout-static">
<default-topbar />
<default-sidebar />
<div class="layout-main-container">
<div class="layout-main">
<slot />
</div>
<default-footer />
</div>
<div class="layout-mask animate-fadein"></div>
</div>
<Toast />
</template>
<script setup lang="ts">
import Toast from "primevue/toast";
import DefaultTopbar from "~/layouts/default/Topbar.vue";
import DefaultSidebar from "~/layouts/default/Sidebar.vue";
import DefaultFooter from "~/layouts/default/Footer.vue";
</script>

View file

@ -0,0 +1,9 @@
<script setup lang="ts">
const currentYear = new Date().getFullYear();
</script>
<template>
<footer class="layout-footer font-bold">
<div v-if="currentYear === 2025">Glowing Fiesta 2025</div>
<div v-else>Glowing Fiesta 2025 - {{ currentYear }}</div>
</footer>
</template>

View file

@ -0,0 +1,35 @@
<template>
<div class="layout-sidebar">
<ul class="layout-menu">
<li class="layout-root-menuitem">
<div class="layout-menuitem-root-text">Home</div>
<ul class="layout-submenu">
<li>
<nuxt-link to="/">
<i class="pi pi-home layout-menuitem-icon"></i>
<span class="layout-menuitem-text">Dashboard</span>
</nuxt-link>
</li>
</ul>
</li>
<li class="layout-root-menuitem">
<div class="layout-menuitem-root-text">Configuration</div>
<ul class="layout-submenu">
<li>
<nuxt-link to="/config/account">
<i class="pi pi-user layout-menuitem-icon"></i>
<span class="layout-menuitem-text">Account</span>
</nuxt-link>
</li>
<li>
<nuxt-link to="/config/settings">
<i class="pi pi-cog layout-menuitem-icon"></i>
<span class="layout-menuitem-text">Settings</span>
</nuxt-link>
</li>
</ul>
</li>
</ul>
</div>
</template>

View file

@ -0,0 +1,56 @@
<script setup lang="ts">
import { ref } from "vue";
const isDarkTheme = ref(false);
const toggleDarkMode = () => {
isDarkTheme.value = !isDarkTheme.value;
document.documentElement.classList.toggle("app-dark");
};
const toggleMenu = () => {
document.getElementsByClassName("layout-wrapper")[0]?.classList.toggle("layout-static-inactive");
};
</script>
<template>
<nav class="layout-topbar">
<div class="layout-topbar-logo-container">
<button class="layout-menu-button layout-topbar-action" @click="toggleMenu">
<i class="pi pi-bars"></i>
</button>
<nuxt-link to="/" class="layout-topbar-logo">
<img src="@/assets/images/logo.png" alt="Two stick silhouettes admiring fireworks in the sky" />
<span>Glowing Fiesta</span>
</nuxt-link>
</div>
<div class="layout-topbar-actions">
<div class="layout-config-menu">
<button type="button" class="layout-topbar-action layout-topbar-action-highlight" @click="toggleDarkMode">
<i :class="['pi', { 'pi-moon': isDarkTheme, 'pi-sun': !isDarkTheme }]"></i>
</button>
</div>
<Divider layout="vertical" />
<div class="layout-topbar-menu hidden lg:block">
<div class="layout-topbar-menu-content">
<button type="button" class="layout-topbar-action">
<i class="pi pi-calendar"></i>
<span>Calendar</span>
</button>
<button type="button" class="layout-topbar-action">
<i class="pi pi-inbox"></i>
<span>Messages</span>
</button>
<button type="button" class="layout-topbar-action">
<i class="pi pi-user"></i>
<span>Profile</span>
</button>
</div>
</div>
</div>
</nav>
</template>

View file

@ -0,0 +1,5 @@
<template>
<div class="card">
<h1>Account</h1>
</div>
</template>

View file

@ -0,0 +1,5 @@
<template>
<div class="card">
<h1>Settings</h1>
</div>
</template>

View file

@ -1,21 +1,5 @@
<template>
<article class="w-3/4 flex flex-col gap-4 m-8 text-center">
<header class="mb-4">
<h1 class="w-full text-4xl font-bold">Homepage</h1>
<p class="w-full">This is your homepage</p>
</header>
<div class="article-content w-full">
<h2 class="w-full text-xl font-bold">First paragraph</h2>
<p class="w-full text-justify mb-4">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam eu ornare velit. Curabitur urna felis, malesuada et sapien
eu, tincidunt consectetur sem. Cras eu tortor nec lorem vehicula sollicitudin. Sed et pellentesque dolor, et faucibus
magna nulla.
</p>
</div>
<footer>
<Button label="Verify PrimeVue" />
</footer>
</article>
<div class="card">
<h1>Dashboard</h1>
</div>
</template>