Init
This commit is contained in:
60
.htaccess
Normal file
60
.htaccess
Normal file
@@ -0,0 +1,60 @@
|
||||
# Enable RewriteEngine
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
|
||||
# Add trailing slash to URLs (except files and directories)
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_URI} !/$
|
||||
RewriteRule ^(.+)$ $1/ [L,R=301]
|
||||
|
||||
# Handle 404 errors
|
||||
ErrorDocument 404 /404
|
||||
|
||||
# Rewrite pretty URLs to index.php?page=
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^([^/]+)/$ index.php?page=$1 [L,QSA]
|
||||
|
||||
# Enable GZip compression
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
|
||||
AddOutputFilterByType DEFLATE application/javascript application/json
|
||||
AddOutputFilterByType DEFLATE application/xml application/xhtml+xml application/rss+xml
|
||||
AddOutputFilterByType DEFLATE application/font-woff application/font-woff2
|
||||
AddOutputFilterByType DEFLATE image/svg+xml image/webp
|
||||
</IfModule>
|
||||
|
||||
# Enable browser caching for static assets
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
ExpiresByType image/jpeg "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType image/webp "access plus 1 year"
|
||||
ExpiresByType image/svg+xml "access plus 1 year"
|
||||
ExpiresByType text/css "access plus 1 month"
|
||||
ExpiresByType application/javascript "access plus 1 month"
|
||||
ExpiresByType image/x-icon "access plus 1 year"
|
||||
ExpiresByType application/font-woff "access plus 1 year"
|
||||
ExpiresByType application/font-woff2 "access plus 1 year"
|
||||
</IfModule>
|
||||
|
||||
# Security headers
|
||||
<IfModule mod_headers.c>
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
Header set X-Frame-Options "DENY"
|
||||
Header set X-XSS-Protection "1; mode=block"
|
||||
</IfModule>
|
||||
|
||||
# Prevent directory listing
|
||||
Options -Indexes
|
||||
|
||||
# BEGIN cPanel-generated php ini directives, do not edit
|
||||
<IfModule php8_module>
|
||||
php_value output_buffering Off
|
||||
</IfModule>
|
||||
<IfModule lsapi_module>
|
||||
php_value output_buffering Off
|
||||
</IfModule>
|
||||
# END cPanel-generated php ini directives, do not edit
|
||||
6
index.php
Normal file
6
index.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/pageHandler.php');
|
||||
include($_SERVER['DOCUMENT_ROOT'] . '/rss/php/includes/header.php');
|
||||
loadContents();
|
||||
include($_SERVER['DOCUMENT_ROOT'] . '/rss/php/includes/footer.php');
|
||||
?>
|
||||
3
robots.txt
Normal file
3
robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
User-agent: *
|
||||
Disallow: /404
|
||||
Sitemap: https://DOMAIN.COM/sitemap.xml
|
||||
6792
rss/css/main.css
Normal file
6792
rss/css/main.css
Normal file
File diff suppressed because it is too large
Load Diff
1
rss/css/main.css.map
Normal file
1
rss/css/main.css.map
Normal file
File diff suppressed because one or more lines are too long
873
rss/css/main.scss
Normal file
873
rss/css/main.scss
Normal file
@@ -0,0 +1,873 @@
|
||||
@import 'variables';
|
||||
@import url('https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,200..1000;1,200..1000&display=swap');
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Reset + Base */
|
||||
/* ========================================================================== */
|
||||
*,
|
||||
*::before,
|
||||
*::after { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
|
||||
html, body { height: 100%; }
|
||||
|
||||
body {
|
||||
font-family: $font-family-base;
|
||||
font-size: map-get($font-sizes, base);
|
||||
line-height: map-get($line-heights, normal);
|
||||
color: map-get($colors, dark);
|
||||
background: map-get($colors, white);
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
a { color: inherit; text-decoration: none; }
|
||||
img, svg, video, canvas { display: block; max-width: 100%; height: auto; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Typography Utilities */
|
||||
/* ========================================================================== */
|
||||
@each $k, $v in $font-sizes { .text-#{$k} { font-size: $v; } }
|
||||
@each $k, $v in $font-weights { .fw-#{$k} { font-weight: $v; } }
|
||||
@each $k, $v in $line-heights { .lh-#{$k} { line-height: $v; } }
|
||||
|
||||
.text-left { text-align: left; }
|
||||
.text-center { text-align: center; }
|
||||
.text-right { text-align: right; }
|
||||
.text-uppercase { text-transform: uppercase; }
|
||||
.text-lowercase { text-transform: lowercase; }
|
||||
.text-capitalize { text-transform: capitalize; }
|
||||
.text-underline { text-decoration: underline; }
|
||||
.text-line-through { text-decoration: line-through; }
|
||||
.text-no-decoration { text-decoration: none; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Color Utilities */
|
||||
/* ========================================================================== */
|
||||
@each $name, $color in $colors {
|
||||
.text-#{$name} { color: $color !important; }
|
||||
.bg-#{$name} { background-color: $color !important; }
|
||||
.border-#{$name} { border-color: $color !important; }
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Spacing Utilities (Margin / Padding) */
|
||||
/* ========================================================================== */
|
||||
@each $k, $v in $space {
|
||||
.m-#{$k} { margin: $v !important; }
|
||||
.mt-#{$k} { margin-top: $v !important; }
|
||||
.mr-#{$k} { margin-right: $v !important; }
|
||||
.mb-#{$k} { margin-bottom: $v !important; }
|
||||
.ml-#{$k} { margin-left: $v !important; }
|
||||
.mx-#{$k} { margin-left: $v !important; margin-right: $v !important; }
|
||||
.my-#{$k} { margin-top: $v !important; margin-bottom: $v !important; }
|
||||
|
||||
.p-#{$k} { padding: $v !important; }
|
||||
.pt-#{$k} { padding-top: $v !important; }
|
||||
.pr-#{$k} { padding-right: $v !important; }
|
||||
.pb-#{$k} { padding-bottom: $v !important; }
|
||||
.pl-#{$k} { padding-left: $v !important; }
|
||||
.px-#{$k} { padding-left: $v !important; padding-right: $v !important; }
|
||||
.py-#{$k} { padding-top: $v !important; padding-bottom: $v !important; }
|
||||
}
|
||||
|
||||
/* Responsive spacing */
|
||||
@each $bp, $w in $breakpoints {
|
||||
@media (min-width: $w) {
|
||||
@each $k, $v in $space {
|
||||
.#{$bp}-m-#{$k} { margin: $v !important; }
|
||||
.#{$bp}-mt-#{$k} { margin-top: $v !important; }
|
||||
.#{$bp}-mr-#{$k} { margin-right: $v !important; }
|
||||
.#{$bp}-mb-#{$k} { margin-bottom: $v !important; }
|
||||
.#{$bp}-ml-#{$k} { margin-left: $v !important; }
|
||||
.#{$bp}-mx-#{$k} { margin-left: $v !important; margin-right: $v !important; }
|
||||
.#{$bp}-my-#{$k} { margin-top: $v !important; margin-bottom: $v !important; }
|
||||
|
||||
.#{$bp}-p-#{$k} { padding: $v !important; }
|
||||
.#{$bp}-pt-#{$k} { padding-top: $v !important; }
|
||||
.#{$bp}-pr-#{$k} { padding-right: $v !important; }
|
||||
.#{$bp}-pb-#{$k} { padding-bottom: $v !important; }
|
||||
.#{$bp}-pl-#{$k} { padding-left: $v !important; }
|
||||
.#{$bp}-px-#{$k} { padding-left: $v !important; padding-right: $v !important; }
|
||||
.#{$bp}-py-#{$k} { padding-top: $v !important; padding-bottom: $v !important; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Sizing Utilities (Width / Height) */
|
||||
/* ========================================================================== */
|
||||
@each $k, $v in $size-percent { .w-#{$k} { width: $v; } .h-#{$k} { height: $v; } }
|
||||
@each $k, $v in $size-px { .wpx-#{$k} { width: $v; } .hpx-#{$k} { height: $v; } }
|
||||
@each $k, $v in $size-rem { .wrem-#{$k} { width: $v; } .hrem-#{$k} { height: $v; } }
|
||||
@each $k, $v in $size-vw { .wvw-#{$k} { width: $v; } }
|
||||
@each $k, $v in $size-vh { .hvh-#{$k} { height: $v; } }
|
||||
@each $k, $v in $size-dvh { .hdvh-#{$k} { height: $v; } }
|
||||
@each $k, $v in $size-svh { .hsvh-#{$k} { height: $v; } }
|
||||
@each $k, $v in $size-lvh { .hlvh-#{$k} { height: $v; } }
|
||||
|
||||
/* Responsive sizing */
|
||||
@each $bp, $w in $breakpoints {
|
||||
@media (min-width: $w) {
|
||||
@each $k, $v in $size-percent { .#{$bp}-w-#{$k} { width: $v; } .#{$bp}-h-#{$k} { height: $v; } }
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Display + Layout */
|
||||
/* ========================================================================== */
|
||||
@each $t in $display-types { .d-#{$t} { display: $t !important; } }
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
@each $bp, $mw in $container-max-widths {
|
||||
@media (min-width: map-get($breakpoints, $bp)) { max-width: $mw; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Container variants */
|
||||
.container-fluid {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.container-bleed {
|
||||
width: 100vw;
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
.container-narrow {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
max-width: clamp(36rem, 70vw, 56rem);
|
||||
}
|
||||
|
||||
.container-wide {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
max-width: clamp(72rem, 92vw, 100rem);
|
||||
}
|
||||
.container-fluid {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.container-bleed {
|
||||
width: 100vw;
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
.container-narrow {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
max-width: clamp(36rem, 70vw, 56rem);
|
||||
}
|
||||
|
||||
.container-wide {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
max-width: clamp(72rem, 92vw, 100rem);
|
||||
}
|
||||
.container-fluid {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
max-width: none;
|
||||
}
|
||||
|
||||
.container-bleed {
|
||||
width: 100vw;
|
||||
margin-left: 50%;
|
||||
transform: translateX(-50%);
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
.container-narrow {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
max-width: clamp(36rem, 70vw, 56rem);
|
||||
}
|
||||
|
||||
.container-wide {
|
||||
width: 100%;
|
||||
margin-inline: auto;
|
||||
padding-inline: $container-padding;
|
||||
max-width: clamp(72rem, 92vw, 100rem);
|
||||
}
|
||||
|
||||
/* Flex grid */
|
||||
.row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-inline: map-get($gutters, 3);
|
||||
margin-block: map-get($gutters, 3);
|
||||
}
|
||||
.col { flex: 1 0 0%; padding-inline: map-get($gutters, 3); padding-block: map-get($gutters, 3); }
|
||||
.col-auto { flex: 0 0 auto; width: auto; padding-inline: map-get($gutters, 3); padding-block: map-get($gutters, 3); }
|
||||
|
||||
@for $i from 1 through $grid-columns {
|
||||
.col-#{$i} {
|
||||
flex: 0 0 percentage($i / $grid-columns);
|
||||
max-width: percentage($i / $grid-columns);
|
||||
padding-inline: map-get($gutters, 3);
|
||||
padding-block: map-get($gutters, 3);
|
||||
}
|
||||
}
|
||||
@each $bp, $w in $breakpoints {
|
||||
@media (min-width: $w) {
|
||||
.col-#{$bp} { flex: 1 0 0%; padding-inline: map-get($gutters, 3); padding-block: map-get($gutters, 3); }
|
||||
.col-#{$bp}-auto { flex: 0 0 auto; width: auto; padding-inline: map-get($gutters, 3); padding-block: map-get($gutters, 3); }
|
||||
@for $i from 1 through $grid-columns {
|
||||
.col-#{$bp}-#{$i} {
|
||||
flex: 0 0 percentage($i / $grid-columns);
|
||||
max-width: percentage($i / $grid-columns);
|
||||
padding-inline: map-get($gutters, 3);
|
||||
padding-block: map-get($gutters, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Flex helpers */
|
||||
.flex { display: flex; }
|
||||
.inline-flex { display: inline-flex; }
|
||||
@each $k, $v in $flex-directions { .flex-#{$k} { display: flex; flex-direction: $v; } }
|
||||
@each $k, $v in $justify-content-values { .justify-#{$k} { display: flex; justify-content: $v; } }
|
||||
@each $k, $v in $align-items-values { .items-#{$k} { display: flex; align-items: $v; } }
|
||||
|
||||
/* Borders + Radius + Shadows */
|
||||
@each $k, $v in $border-widths { .border-#{$k} { border-width: $v; border-style: solid; border-color: currentColor; } }
|
||||
.border { border: 1px solid currentColor; }
|
||||
.border-top { border-top: 1px solid currentColor; }
|
||||
.border-right { border-right: 1px solid currentColor; }
|
||||
.border-bottom { border-bottom: 1px solid currentColor; }
|
||||
.border-left { border-left: 1px solid currentColor; }
|
||||
|
||||
@each $k, $v in $radii { .rounded-#{$k} { border-radius: $v; } }
|
||||
.rounded { border-radius: map-get($radii, md); }
|
||||
.rounded-top { border-top-left-radius: map-get($radii, md); border-top-right-radius: map-get($radii, md); }
|
||||
.rounded-bottom { border-bottom-left-radius: map-get($radii, md); border-bottom-right-radius: map-get($radii, md); }
|
||||
.rounded-left { border-top-left-radius: map-get($radii, md); border-bottom-left-radius: map-get($radii, md); }
|
||||
.rounded-right { border-top-right-radius: map-get($radii, md); border-bottom-right-radius: map-get($radii, md); }
|
||||
|
||||
@each $k, $v in $shadows { .shadow-#{$k} { box-shadow: $v !important; } }
|
||||
@each $k, $v in $drop-shadows { .drop-shadow-#{$k} { box-shadow: $v !important; } }
|
||||
|
||||
/* Z-Index */
|
||||
.z-base { z-index: map-get($z, base); }
|
||||
.z-dropdown { z-index: map-get($z, dropdown); }
|
||||
.z-overlay { z-index: map-get($z, overlay); }
|
||||
.z-modal { z-index: map-get($z, modal); }
|
||||
.z-toast { z-index: map-get($z, toast); }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Grid System (CSS Grid Utilities) */
|
||||
/* ========================================================================== */
|
||||
.grid { display: grid !important; }
|
||||
.inline-grid { display: inline-grid !important; }
|
||||
|
||||
@for $i from 1 through 12 { .grid-cols-#{$i} { grid-template-columns: repeat($i, 1fr) !important; } }
|
||||
.grid-cols-none { grid-template-columns: none !important; }
|
||||
|
||||
@each $bp, $width in $breakpoints {
|
||||
@media (min-width: $width) {
|
||||
@for $i from 1 through 12 {
|
||||
.#{$bp}-grid-cols-#{$i} { grid-template-columns: repeat($i, 1fr) !important; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@for $i from 1 through 12 { .grid-rows-#{$i} { grid-template-rows: repeat($i, 1fr) !important; } }
|
||||
.grid-rows-none { grid-template-rows: none !important; }
|
||||
|
||||
.grid-auto-fit-sm { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)) !important; }
|
||||
.grid-auto-fit-md { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)) !important; }
|
||||
.grid-auto-fit-lg { grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)) !important; }
|
||||
.grid-auto-fill-sm { grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)) !important; }
|
||||
.grid-auto-fill-md { grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)) !important; }
|
||||
.grid-auto-fill-lg { grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)) !important; }
|
||||
|
||||
.grid-align-center { align-items: center !important; justify-items: center !important; }
|
||||
.grid-align-start { align-items: start !important; justify-items: start !important; }
|
||||
.grid-align-end { align-items: end !important; justify-items: end !important; }
|
||||
.grid-align-stretch { align-items: stretch !important; justify-items: stretch !important; }
|
||||
|
||||
.place-self-center { place-self: center !important; }
|
||||
.place-self-start { place-self: start !important; }
|
||||
.place-self-end { place-self: end !important; }
|
||||
.place-self-stretch { place-self: stretch !important; }
|
||||
|
||||
.grid-flow-row { grid-auto-flow: row !important; }
|
||||
.grid-flow-col { grid-auto-flow: column !important; }
|
||||
.grid-flow-row-dense { grid-auto-flow: row dense !important; }
|
||||
.grid-flow-col-dense { grid-auto-flow: column dense !important; }
|
||||
|
||||
/* Auto rows/cols sizing */
|
||||
.grid-auto-cols-min { grid-auto-columns: min-content !important; }
|
||||
.grid-auto-cols-max { grid-auto-columns: max-content !important; }
|
||||
.grid-auto-cols-fr { grid-auto-columns: 1fr !important; }
|
||||
.grid-auto-rows-min { grid-auto-rows: min-content !important; }
|
||||
.grid-auto-rows-max { grid-auto-rows: max-content !important; }
|
||||
.grid-auto-rows-fr { grid-auto-rows: 1fr !important; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Control Blueprints (shared by inputs/buttons/etc.) */
|
||||
/* ========================================================================== */
|
||||
@mixin control-base {
|
||||
appearance: none;
|
||||
border-style: map-get($control-border, style);
|
||||
border-width: map-get($control-border, width);
|
||||
border-color: map-get($control-colors, border);
|
||||
background-color: map-get($control-colors, bg);
|
||||
color: map-get($control-colors, fg);
|
||||
outline: 0;
|
||||
transition:
|
||||
background-color map-get($timings, base) map-get($easings, in-out),
|
||||
color map-get($timings, base) map-get($easings, in-out),
|
||||
border-color map-get($timings, base) map-get($easings, in-out),
|
||||
box-shadow map-get($timings, base) map-get($easings, in-out),
|
||||
transform map-get($timings, fast) map-get($easings, out);
|
||||
}
|
||||
|
||||
@mixin control-size($size) {
|
||||
$props: map-get($control-sizes, $size);
|
||||
font-size: map-get($props, font-size);
|
||||
line-height: map-get($props, line-height);
|
||||
padding: map-get($props, py) map-get($props, px);
|
||||
border-radius: map-get($props, radius);
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
.input, select, textarea {
|
||||
@include control-base;
|
||||
width: 100%;
|
||||
&:focus {
|
||||
border-color: map-get($control-colors, focus);
|
||||
box-shadow: 0 0 0 3px map-get($control-colors, ring);
|
||||
background-color: map-get($colors, white);
|
||||
}
|
||||
&:disabled {
|
||||
background-color: map-get($control-colors, disabled-bg);
|
||||
opacity: .6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
&::placeholder { color: map-get($control-colors, placeholder); }
|
||||
}
|
||||
@each $size, $props in $control-sizes { .input-#{$size} { @include control-size($size); } }
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
@include control-base;
|
||||
display: inline-flex;
|
||||
width: auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
box-shadow: map-get($shadows, 1);
|
||||
&:focus-visible {
|
||||
border-color: map-get($control-colors, focus);
|
||||
box-shadow: 0 0 0 3px map-get($control-colors, ring), map-get($shadows, 2);
|
||||
}
|
||||
&:active { transform: translateY(1px); box-shadow: map-get($shadows, 3); }
|
||||
&:disabled { opacity: .6; cursor: not-allowed; pointer-events: none; }
|
||||
}
|
||||
@each $size, $props in $control-sizes { .btn-#{$size} { @include control-size($size); } }
|
||||
|
||||
@each $name, $color in $colors {
|
||||
.btn-#{$name} {
|
||||
background-color: $color;
|
||||
color: if(lightness($color) > 60%, map-get($colors, dark), map-get($colors, white));
|
||||
border-color: transparent;
|
||||
&:hover { background-color: darken($color, 8%); box-shadow: map-get($shadows, 2); }
|
||||
}
|
||||
.btn-outline-#{$name} {
|
||||
background-color: transparent;
|
||||
color: $color;
|
||||
border-color: $color;
|
||||
&:hover { background-color: $color; color: if(lightness($color) > 60%, map-get($colors, dark), map-get($colors, white)); }
|
||||
}
|
||||
.btn-ghost-#{$name} {
|
||||
background-color: transparent;
|
||||
color: $color;
|
||||
border-color: transparent;
|
||||
&:hover { background-color: rgba($color, .12); }
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Alerts */
|
||||
/* ========================================================================== */
|
||||
.alert {
|
||||
padding: map-get($space, 2) map-get($space, 3);
|
||||
border-radius: map-get($radii, md);
|
||||
border: 1px solid map-get($control-colors, border);
|
||||
background: map-get($colors, accent-white);
|
||||
}
|
||||
@each $name, $color in $colors {
|
||||
.alert-#{$name} {
|
||||
background: mix($color, map-get($colors, white), 12%);
|
||||
border-color: $color;
|
||||
color: if(lightness($color) > 60%, map-get($colors, dark), map-get($colors, dark));
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Cards (with custom borders and shadows) */
|
||||
/* ========================================================================== */
|
||||
.card {
|
||||
background: map-get($colors, white);
|
||||
border: 1px solid map-get($control-colors, border);
|
||||
border-radius: map-get($radii, lg);
|
||||
box-shadow: map-get($shadows, 1);
|
||||
transition: box-shadow map-get($timings, base) map-get($easings, in-out);
|
||||
}
|
||||
.card-hover:hover { box-shadow: map-get($shadows, 3); }
|
||||
|
||||
@each $k, $v in $shadows { .card-shadow-#{$k} { box-shadow: $v; } }
|
||||
@each $name, $color in $colors {
|
||||
.card-border-#{$name} { border-color: $color; }
|
||||
.card-bg-#{$name} { background-color: $color; }
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Modals */
|
||||
/* ========================================================================== */
|
||||
.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: map-get($space, 3);
|
||||
z-index: map-get($z, modal);
|
||||
}
|
||||
.modal.open { display: flex; }
|
||||
|
||||
.modal__overlay { position: absolute; inset: 0; background: rgba(map-get($colors, black), .5); }
|
||||
|
||||
.modal__dialog {
|
||||
position: relative;
|
||||
width: min(720px, 100%);
|
||||
background: map-get($colors, white);
|
||||
border: 1px solid map-get($control-colors, border);
|
||||
border-radius: map-get($radii, lg);
|
||||
box-shadow: map-get($shadows, 5);
|
||||
padding: map-get($space, 3);
|
||||
}
|
||||
|
||||
.modal__header { display: flex; align-items: center; justify-content: space-between; gap: map-get($space, 2); margin-bottom: map-get($space, 2); }
|
||||
.modal__title { font-size: map-get($font-sizes, lg); font-weight: map-get($font-weights, semibold); }
|
||||
.modal__close { @extend .btn; @extend .btn-sm; background: transparent; box-shadow: none; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Pills */
|
||||
/* ========================================================================== */
|
||||
.pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: map-get($space, 1);
|
||||
padding: map-get($space, 1) map-get($space, 2);
|
||||
border-radius: map-get($radii, full);
|
||||
border: 1px solid map-get($control-colors, border);
|
||||
background: map-get($colors, accent-white);
|
||||
font-size: map-get($font-sizes, sm);
|
||||
line-height: map-get($line-heights, normal);
|
||||
}
|
||||
@each $name, $color in $colors {
|
||||
.pill-#{$name} {
|
||||
background: mix($color, map-get($colors, white), 12%);
|
||||
color: if(lightness($color) > 60%, map-get($colors, dark), map-get($colors, white));
|
||||
border-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Accordions */
|
||||
/* ========================================================================== */
|
||||
.accordion { border: 1px solid map-get($control-colors, border); border-radius: map-get($radii, lg); overflow: hidden; }
|
||||
.accordion__item + .accordion__item { border-top: 1px solid map-get($control-colors, border); }
|
||||
|
||||
.accordion__header {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: map-get($space, 2);
|
||||
padding: map-get($space, 2) map-get($space, 3);
|
||||
cursor: pointer;
|
||||
background: map-get($colors, accent-white);
|
||||
border: none;
|
||||
}
|
||||
.accordion__title { font-weight: map-get($font-weights, medium); }
|
||||
.accordion__icon { transition: transform map-get($timings, base) map-get($easings, in-out); }
|
||||
|
||||
.accordion__panel {
|
||||
display: grid;
|
||||
grid-template-rows: 0fr;
|
||||
transition: grid-template-rows map-get($timings, base) map-get($easings, in-out);
|
||||
}
|
||||
.accordion__content { overflow: hidden; padding: 0 map-get($space, 3); }
|
||||
|
||||
.accordion__item.is-open .accordion__panel { grid-template-rows: 1fr; }
|
||||
.accordion__item.is-open .accordion__content { padding: map-get($space, 2) map-get($space, 3) map-get($space, 3); }
|
||||
.accordion__item.is-open .accordion__icon { transform: rotate(180deg); }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Stack Utilities */
|
||||
/* ========================================================================== */
|
||||
.stack > * + * { margin-top: map-get($space, 3); }
|
||||
@each $k, $v in $space { .stack-#{$k} > * + * { margin-top: $v; } }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Transitions & Animations */
|
||||
/* ========================================================================== */
|
||||
.transition-all { transition: all map-get($timings, base) map-get($easings, in-out); }
|
||||
.transition-fast { transition-duration: map-get($timings, fast); }
|
||||
.transition-slow { transition-duration: map-get($timings, slow); }
|
||||
|
||||
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
|
||||
@keyframes slide-up { from { transform: translateY(10px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
|
||||
|
||||
.animate-fade-in { animation: fade-in .3s map-get($easings, out) both; }
|
||||
.animate-slide-up { animation: slide-up .35s map-get($easings, out) both; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Tooltips */
|
||||
/* ========================================================================== */
|
||||
.tooltip { position: relative; display: inline-block; cursor: help; }
|
||||
.tooltip::after {
|
||||
content: attr(data-tip);
|
||||
position: absolute;
|
||||
bottom: calc(100% + 6px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: map-get($colors, dark);
|
||||
color: map-get($colors, white);
|
||||
padding: .25rem .5rem;
|
||||
font-size: map-get($font-sizes, sm);
|
||||
border-radius: map-get($radii, sm);
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity map-get($timings, base) map-get($easings, in-out);
|
||||
z-index: map-get($z, dropdown);
|
||||
}
|
||||
.tooltip:hover::after { opacity: 1; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Tabs */
|
||||
/* ========================================================================== */
|
||||
.tabs { display: flex; gap: map-get($space, 2); border-bottom: 1px solid map-get($control-colors, border); }
|
||||
.tab {
|
||||
padding: map-get($space, 2) map-get($space, 3);
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid transparent;
|
||||
transition:
|
||||
border-color map-get($timings, base) map-get($easings, in-out),
|
||||
color map-get($timings, base) map-get($easings, in-out);
|
||||
}
|
||||
.tab.active { border-color: map-get($colors, primary); color: map-get($colors, primary); }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Transform & Scale Utilities */
|
||||
/* ========================================================================== */
|
||||
.scale-90 { transform: scale(.9); }
|
||||
.scale-95 { transform: scale(.95); }
|
||||
.scale-100 { transform: scale(1); }
|
||||
.scale-105 { transform: scale(1.05); }
|
||||
.scale-110 { transform: scale(1.1); }
|
||||
|
||||
.rotate-90 { transform: rotate(90deg); }
|
||||
.rotate-180 { transform: rotate(180deg); }
|
||||
.rotate-270 { transform: rotate(270deg); }
|
||||
|
||||
.skew-x-6 { transform: skewX(6deg); }
|
||||
.skew-y-6 { transform: skewY(6deg); }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Shadow, Blur, and Glow Utilities */
|
||||
/* ========================================================================== */
|
||||
.shadow-none { box-shadow: none !important; }
|
||||
.blur-sm { filter: blur(2px); }
|
||||
.blur-md { filter: blur(4px); }
|
||||
.blur-lg { filter: blur(8px); }
|
||||
.glow { box-shadow: 0 0 12px rgba(map-get($colors, accent), .5); }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Dark Mode (opt-in via .theme-dark) */
|
||||
/* ========================================================================== */
|
||||
.theme-dark { background: map-get($colors, dark); color: map-get($colors, light); }
|
||||
.theme-dark .card,
|
||||
.theme-dark .modal__dialog {
|
||||
background: map-get($colors, secondary);
|
||||
border-color: map-get($colors, muted);
|
||||
color: map-get($colors, light);
|
||||
}
|
||||
.theme-dark .input,
|
||||
.theme-dark select,
|
||||
.theme-dark textarea {
|
||||
background: map-get($colors, secondary);
|
||||
color: map-get($colors, light);
|
||||
border-color: map-get($colors, muted);
|
||||
}
|
||||
.theme-dark .tabs { border-bottom-color: map-get($colors, muted); }
|
||||
.theme-dark .tab.active { border-color: map-get($colors, primary); color: map-get($colors, primary); }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Scroll Utilities */
|
||||
/* ========================================================================== */
|
||||
.scroll-y { overflow-y: auto !important; }
|
||||
.scroll-x { overflow-x: auto !important; }
|
||||
.scroll-hidden { overflow: hidden !important; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Spinner */
|
||||
/* ========================================================================== */
|
||||
@keyframes ui-spinner { to { transform: rotate(360deg); } }
|
||||
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
border-radius: 50%;
|
||||
border-style: solid;
|
||||
border-width: 2px;
|
||||
border-color: map-get($colors, dark);
|
||||
border-left-color: transparent !important;
|
||||
animation: ui-spinner 1s linear infinite;
|
||||
}
|
||||
@each $name, $color in $colors {
|
||||
.spinner-#{$name} { border-color: $color; border-left-color: transparent !important; }
|
||||
}
|
||||
.spinner-sm { width: .875rem; height: .875rem; border-width: 2px; }
|
||||
.spinner-md { width: 1.25rem; height: 1.25rem; border-width: 2px; }
|
||||
.spinner-lg { width: 1.75rem; height: 1.75rem; border-width: 3px; }
|
||||
.spinner-xl { width: 2.25rem; height: 2.25rem; border-width: 4px; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Visibility + Accessibility */
|
||||
/* ========================================================================== */
|
||||
.visible { visibility: visible !important; }
|
||||
.invisible { visibility: hidden !important; }
|
||||
|
||||
.sr-only {
|
||||
position: absolute !important;
|
||||
width: 1px !important;
|
||||
height: 1px !important;
|
||||
padding: 0 !important;
|
||||
margin: -1px !important;
|
||||
overflow: hidden !important;
|
||||
clip: rect(0, 0, 0, 0) !important;
|
||||
white-space: nowrap !important;
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Overflow + Text Truncation */
|
||||
/* ========================================================================== */
|
||||
.overflow-hidden { overflow: hidden !important; }
|
||||
.overflow-auto { overflow: auto !important; }
|
||||
.overflow-scroll { overflow: scroll !important; }
|
||||
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Opacity */
|
||||
/* ========================================================================== */
|
||||
.opacity-0 { opacity: 0; }
|
||||
.opacity-25 { opacity: .25; }
|
||||
.opacity-50 { opacity: .5; }
|
||||
.opacity-75 { opacity: .75; }
|
||||
.opacity-100 { opacity: 1; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Cursor + Pointer Events */
|
||||
/* ========================================================================== */
|
||||
.cursor-default { cursor: default !important; }
|
||||
.cursor-pointer { cursor: pointer !important; }
|
||||
.cursor-not-allowed { cursor: not-allowed !important; }
|
||||
.pointer-none { pointer-events: none !important; }
|
||||
.pointer-auto { pointer-events: auto !important; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Position + Inset */
|
||||
/* ========================================================================== */
|
||||
.relative { position: relative !important; }
|
||||
.absolute { position: absolute !important; }
|
||||
.fixed { position: fixed !important; }
|
||||
.sticky { position: sticky !important; }
|
||||
|
||||
.inset-0 { inset: 0 !important; }
|
||||
.top-0 { top: 0 !important; }
|
||||
.right-0 { right: 0 !important; }
|
||||
.bottom-0 { bottom: 0 !important; }
|
||||
.left-0 { left: 0 !important; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Flex Wrapping + Gap */
|
||||
/* ========================================================================== */
|
||||
.wrap { flex-wrap: wrap !important; }
|
||||
.nowrap { flex-wrap: nowrap !important; }
|
||||
.wrap-reverse { flex-wrap: wrap-reverse !important; }
|
||||
|
||||
@each $k, $v in $space {
|
||||
.gap-#{$k} { gap: $v !important; }
|
||||
.row-gap-#{$k} { row-gap: $v !important; }
|
||||
.col-gap-#{$k} { column-gap: $v !important; }
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Responsive Reverse (small-down) */
|
||||
/* ========================================================================== */
|
||||
.sm-reverse {
|
||||
@media (max-width: map-get($breakpoints, md)) {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Aspect Ratios + Object Fit */
|
||||
/* ========================================================================== */
|
||||
.ratio { position: relative; width: 100%; height: 0; overflow: hidden; }
|
||||
.ratio > * { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||||
|
||||
.ratio-16x9 { padding-bottom: 56.25%; }
|
||||
.ratio-4x3 { padding-bottom: 75%; }
|
||||
.ratio-1x1 { padding-bottom: 100%; }
|
||||
|
||||
.object-contain { object-fit: contain !important; }
|
||||
.object-cover { object-fit: cover !important; }
|
||||
.object-fill { object-fit: fill !important; }
|
||||
.object-none { object-fit: none !important; }
|
||||
.object-scale-down { object-fit: scale-down !important; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Hover Motion Helpers */
|
||||
/* ========================================================================== */
|
||||
.hover-raise { transition: transform map-get($timings, base) map-get($easings, in-out), box-shadow map-get($timings, base) map-get($easings, in-out); }
|
||||
.hover-raise:hover { transform: translateY(-2px); box-shadow: map-get($shadows, 2); }
|
||||
|
||||
.hover-glow { transition: box-shadow map-get($timings, base) map-get($easings, in-out); }
|
||||
.hover-glow:hover { box-shadow: 0 0 0 3px map-get($control-colors, ring), map-get($shadows, 2); }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Divider Utilities */
|
||||
/* ========================================================================== */
|
||||
.hr { width: 100%; height: 1px; background: map-get($control-colors, border); border: 0; }
|
||||
.hr-dashed { height: 1px; border-top: 1px dashed map-get($control-colors, border); background: transparent; }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Utility Badges (aliases of pills) */
|
||||
/* ========================================================================== */
|
||||
.badge { @extend .pill; }
|
||||
@each $name, $color in $colors { .badge-#{$name} { @extend .pill-#{$name}; } }
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Quick Utility Aliases */
|
||||
/* ========================================================================== */
|
||||
.rel { position: relative; }
|
||||
.no-click { pointer-events: none; }
|
||||
.center { display: grid; place-items: center; }
|
||||
.full-height { height: 100vh; }
|
||||
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Flex Grid Extensions */
|
||||
/* ========================================================================== */
|
||||
|
||||
.row.no-gutter {
|
||||
margin-inline: 0;
|
||||
margin-block: 0;
|
||||
|
||||
> .col,
|
||||
> [class^="col-"] {
|
||||
padding-inline: 0;
|
||||
padding-block: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.row.gx-0 {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
|
||||
> .col,
|
||||
> [class^="col-"] {
|
||||
padding-right: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.row.gy-0 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
|
||||
> .col,
|
||||
> [class^="col-"] {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.row-bleed {
|
||||
margin-right: calc(#{$container-padding} * -1);
|
||||
margin-left: calc(#{$container-padding} * -1);
|
||||
}
|
||||
|
||||
@each $bp, $width in $breakpoints {
|
||||
@media (min-width: $width) {
|
||||
.#{$bp}-row-center { justify-content: center; }
|
||||
.#{$bp}-row-between { justify-content: space-between; }
|
||||
.#{$bp}-row-around { justify-content: space-around; }
|
||||
.#{$bp}-row-evenly { justify-content: space-evenly; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ========================================================================== */
|
||||
/* CSS Grid Enhancements */
|
||||
/* ========================================================================== */
|
||||
|
||||
@for $i from 1 through 12 {
|
||||
.grid-cols-#{$i} { grid-template-columns: repeat($i, 1fr) !important; }
|
||||
}
|
||||
|
||||
.grid-cols-none { grid-template-columns: none !important; }
|
||||
|
||||
@for $i from 1 through 12 {
|
||||
.grid-rows-#{$i} { grid-template-rows: repeat($i, 1fr) !important; }
|
||||
}
|
||||
|
||||
.grid-rows-none { grid-template-rows: none !important; }
|
||||
|
||||
.grid-auto-fit-sm { grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)) !important; }
|
||||
.grid-auto-fit-md { grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)) !important; }
|
||||
.grid-auto-fit-lg { grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)) !important; }
|
||||
|
||||
.grid-auto-fill-sm { grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)) !important; }
|
||||
.grid-auto-fill-md { grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)) !important; }
|
||||
.grid-auto-fill-lg { grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)) !important; }
|
||||
|
||||
.grid-align-center { align-items: center !important; justify-items: center !important; }
|
||||
.grid-align-start { align-items: start !important; justify-items: start !important; }
|
||||
.grid-align-end { align-items: end !important; justify-items: end !important; }
|
||||
.grid-align-stretch { align-items: stretch !important; justify-items: stretch !important; }
|
||||
|
||||
.grid-flow-row { grid-auto-flow: row !important; }
|
||||
.grid-flow-col { grid-auto-flow: column !important; }
|
||||
.grid-flow-row-dense { grid-auto-flow: row dense !important; }
|
||||
.grid-flow-col-dense { grid-auto-flow: column dense !important; }
|
||||
528
rss/css/theme.css
Normal file
528
rss/css/theme.css
Normal file
@@ -0,0 +1,528 @@
|
||||
/* Grid system */
|
||||
/* Breakpoints */
|
||||
/* Display types */
|
||||
/* Color palette */
|
||||
/* Spacing scale */
|
||||
/* Shadows (soft shadows) */
|
||||
/* Flex utilities */
|
||||
/* Button component variables */
|
||||
/* Typography */
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.7rem 2rem;
|
||||
background-color: #ffffff;
|
||||
align-items: center;
|
||||
box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.12);
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
font-family: "Nunito", sans-serif;
|
||||
background-color: #fff;
|
||||
}
|
||||
header .logo {
|
||||
width: 100px;
|
||||
display: flex;
|
||||
z-index: 999;
|
||||
cursor: pointer;
|
||||
background-color: #fff;
|
||||
}
|
||||
header .logo img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
header nav {
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: -1;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
header nav {
|
||||
top: -200%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
header nav.open {
|
||||
top: 99%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
transition: all 0.3s;
|
||||
background-color: #fff;
|
||||
height: auto;
|
||||
z-index: -1;
|
||||
opacity: 1;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
header nav.open a {
|
||||
padding: 7px 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
header nav .menu-item {
|
||||
color: #000000;
|
||||
font-weight: 500;
|
||||
margin-right: 15px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
transition: color 0.3s;
|
||||
font-size: 16px;
|
||||
}
|
||||
header nav .menu-item.active {
|
||||
font-weight: bold;
|
||||
}
|
||||
header nav .menu-item::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 120%;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background: #aa0b3d;
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
header nav .menu-item:hover {
|
||||
color: #aa0b3d;
|
||||
cursor: pointer;
|
||||
}
|
||||
header nav .menu-item:hover::after {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
header .mobile-toggler {
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
}
|
||||
@media (min-width: 769px) {
|
||||
header .mobile-toggler {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.home-banner {
|
||||
height: calc(100vh - 55px);
|
||||
padding: 50px 0;
|
||||
display: flex;
|
||||
background-color: #ffffff;
|
||||
opacity: 0;
|
||||
animation: slideUp 1s ease-in-out forwards;
|
||||
}
|
||||
.home-banner.no-height {
|
||||
height: auto !important;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.home-banner {
|
||||
height: 100vh;
|
||||
}
|
||||
}
|
||||
.home-banner .home-banner-inner {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.home-banner .home-banner-inner .container {
|
||||
height: 100%;
|
||||
}
|
||||
.home-banner .home-banner-inner .inner-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
height: 100%;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.home-banner .home-banner-inner .inner-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
.home-banner .home-banner-inner .image-col {
|
||||
flex: 0 0 45%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.home-banner .home-banner-inner .image-col img {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
.home-banner .home-banner-inner .text-col {
|
||||
flex: 0 0 40%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.home-banner .home-banner-inner .text-col {
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
z-index: 9;
|
||||
}
|
||||
}
|
||||
.home-banner .home-banner-inner .text-col .title {
|
||||
font-weight: 800;
|
||||
font-size: 3.7rem;
|
||||
line-height: 1.2;
|
||||
font-family: "Nunito", sans-serif;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.home-banner .home-banner-inner .text-col .title {
|
||||
font-size: 3.2rem;
|
||||
}
|
||||
}
|
||||
.home-banner .home-banner-inner .text-col .lead {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.home-banner .home-banner-inner .text-col .lead {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
.home-banner .home-banner-inner .text-col .cta-btn {
|
||||
padding: 10px;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
.home-banner .home-banner-inner .text-col .login-text {
|
||||
display: inline-flex;
|
||||
gap: 5px;
|
||||
font-weight: 400;
|
||||
}
|
||||
.home-banner .home-banner-inner .text-col .login-text .login-link {
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
.home-banner .home-banner-inner .image-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.home-banner .home-banner-inner .image-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
opacity: 0.1;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.home-banner .home-banner-inner .image-wrapper img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.display-banner {
|
||||
width: 100%;
|
||||
padding: 120px 0px;
|
||||
}
|
||||
.display-banner.wedding-banner {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.8)), url("/rss/img/wedding.jpg");
|
||||
padding: 180px 0px;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
}
|
||||
.display-banner.kissing-banner {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.8)), url("/rss/img/couple-kissing.jpg");
|
||||
padding: 180px 0px;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
}
|
||||
.display-banner.moving-banner {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.8)), url("/rss/img/couple.jpg");
|
||||
background-position: center 40%;
|
||||
background-size: cover;
|
||||
padding: 180px 0px;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: #212529;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
padding: 25px 0px;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
}
|
||||
footer .logo {
|
||||
width: 125px;
|
||||
cursor: pointer;
|
||||
}
|
||||
footer ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.features-section h2 {
|
||||
font-size: 1.8rem;
|
||||
color: #212529;
|
||||
}
|
||||
.features-section .theme-card {
|
||||
border-radius: 0.25rem;
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
.features-section .theme-card .card-header {
|
||||
padding: 0.5rem;
|
||||
text-align: center;
|
||||
}
|
||||
.features-section .theme-card .card-header img {
|
||||
width: 125px;
|
||||
height: 125px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.16);
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.features-section .theme-card .card-header img.object-left {
|
||||
-o-object-position: left center;
|
||||
object-position: left center;
|
||||
}
|
||||
.features-section .theme-card .card-header img.object-center {
|
||||
-o-object-position: center center;
|
||||
object-position: center center;
|
||||
}
|
||||
.features-section .theme-card .card-header img.object-bottom {
|
||||
-o-object-position: bottom center;
|
||||
object-position: bottom center;
|
||||
}
|
||||
.features-section .theme-card .card-body {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.styled-title {
|
||||
font-family: "Nunito", sans-serif;
|
||||
}
|
||||
.styled-title.fw-900 {
|
||||
font-weight: 900;
|
||||
}
|
||||
.styled-title.fw-600 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 0.25rem;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.16);
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
height: 100%;
|
||||
margin: 15px 0px;
|
||||
}
|
||||
|
||||
.filter-icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #aa0b3d;
|
||||
}
|
||||
|
||||
.filter-title {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.filter-text {
|
||||
font-size: 1rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.filter-card {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.filter-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
.filter-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
.rounded-col-image {
|
||||
height: 450px;
|
||||
width: 450px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
}
|
||||
@media (max-width: 1200px) {
|
||||
.rounded-col-image {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 920px) {
|
||||
.rounded-col-image {
|
||||
width: 225px;
|
||||
height: 225px;
|
||||
}
|
||||
}
|
||||
.rounded-col-image:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
box-shadow: inset 0px 0px 27px -5px rgb(2, 2, 2);
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
.rounded-col-image img {
|
||||
-o-object-fit: cover;
|
||||
object-fit: cover;
|
||||
-o-object-position: right;
|
||||
object-position: right;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
section h2 {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
section h3 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
section p {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
}
|
||||
section .bolder {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.section-animation {
|
||||
opacity: 0;
|
||||
transform: translateY(50px);
|
||||
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
|
||||
}
|
||||
|
||||
.section-animation.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.user-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - 110px);
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-container {
|
||||
height: auto;
|
||||
padding: 20px 0px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-container.alt .user-card {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
.user-container .user-card {
|
||||
border-radius: 12px;
|
||||
background-color: rgba(255, 255, 255, 0.9058823529);
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-container .user-card {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
.user-container .user-card .form {
|
||||
background-color: #FFF;
|
||||
padding: 20px 0px;
|
||||
flex: 40%;
|
||||
max-width: 40%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.18);
|
||||
border-radius: 12px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-container .user-card .form {
|
||||
flex: 100%;
|
||||
max-width: 100%;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.16);
|
||||
}
|
||||
}
|
||||
.user-container .user-card .form .user-form {
|
||||
padding: 10px 15px;
|
||||
width: 100%;
|
||||
}
|
||||
.user-container .user-card .text-col {
|
||||
flex: 60%;
|
||||
max-width: 60%;
|
||||
padding: 30px 20px;
|
||||
}
|
||||
@media (max-width: 768px) {
|
||||
.user-container .user-card .text-col {
|
||||
flex: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
.user-container .user-card .text-col .logo {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
.user-container .user-card .text-col .logo img {
|
||||
width: 180px;
|
||||
}
|
||||
.user-container .user-card .text-col .text {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.section-animation {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(50px);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}/*# sourceMappingURL=theme.css.map */
|
||||
1
rss/css/theme.css.map
Normal file
1
rss/css/theme.css.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["variables.scss","theme.scss","theme.css"],"names":[],"mappings":"AAAA,gBAAA;AAIA,gBAAA;AAmBA,kBAAA;AAGA,kBAAA;AAoBA,kBAAA;AAGA,2BAAA;AAoBA,mBAAA;AAyBA,+BAAA;AAQA,eAAA;ACpGA;EACE,aAAA;EACA,8BAAA;EACA,oBAAA;EACA,yBAAA;EACA,mBAAA;EACA,+CAAA;EACA,kBAAA;EACA,YAAA;EACA,iCAAA;EACA,sBAAA;ACQF;ADFE;EACE,YAAA;EACA,aAAA;EACA,YAAA;EACA,eAAA;EACA,sBAAA;ACIJ;ADFI;EACE,WAAA;EACA,YAAA;ACIN;ADAE;EACE,aAAA;EACA,kBAAA;EACA,WAAA;ACEJ;ADAI;EALF;IAMI,UAAA;IACA,aAAA;IACA,sBAAA;IACA,kBAAA;IACA,UAAA;IACA,oBAAA;ECGJ;EDDI;IACE,QAAA;IACA,OAAA;IACA,WAAA;IACA,oBAAA;IAEA,sBAAA;IACA,YAAA;IACA,WAAA;IACA,UAAA;IACA,oBAAA;IACA,+CAAA;ECEN;EDAM;IACE,iBAAA;IACA,eAAA;ECER;AACF;ADEI;EACE,cAAA;EACA,gBAAA;EACA,kBAAA;EACA,qBAAA;EACA,kBAAA;EACA,sBAAA;EACA,eAAA;ACAN;ADEM;EACE,iBAAA;ACAR;ADIM;EACE,WAAA;EACA,kBAAA;EACA,SAAA;EACA,OAAA;EACA,WAAA;EACA,WAAA;EACA,mBAAA;EACA,oBAAA;EACA,sBAAA;EACA,0BAAA;ACFR;ADKM;EACE,cAAA;EACA,eAAA;ACHR;ADMM;EACE,oBAAA;ACJR;ADSE;EACE,sBAAA;EACA,YAAA;EACA,eAAA;ACPJ;ADSI;EALF;IAMI,aAAA;ECNJ;AACF;;ADUA;EACE,0BAAA;EACA,eAAA;EACA,aAAA;EACA,yBAAA;EACA,UAAA;EACA,0CAAA;ACPF;ADUE;EACE,uBAAA;ACRJ;ADWE;EAbF;IAcI,aAAA;ECRF;AACF;ADUE;EACE,OAAA;EACA,aAAA;EACA,mBAAA;EACA,WAAA;ACRJ;ADWI;EACE,YAAA;ACTN;ADYI;EACE,aAAA;EACA,8BAAA;EACA,oBAAA;EACA,YAAA;ACVN;ADYM;EANF;IAOI,sBAAA;ECTN;AACF;ADYI;EACE,aAAA;EACA,aAAA;EACA,sBAAA;EACA,uBAAA;ACVN;ADYM;EACE,mBAAA;EACA,0CAAA;ACVR;ADcI;EACE,aAAA;EACA,aAAA;EACA,sBAAA;EACA,uBAAA;EACA,SAAA;ACZN;ADcM;EAPF;IAQI,cAAA;IACA,eAAA;IACA,kBAAA;IACA,UAAA;ECXN;AACF;ADaM;EACE,gBAAA;EACA,iBAAA;EACA,gBAAA;EACA,iCD/EW;AEoEnB;ADaQ;EANF;IAOI,iBAAA;ECVR;AACF;ADaM;EACE,iBAAA;EACA,gBAAA;ACXR;ADaQ;EAJF;IAKI,iBAAA;ECVR;AACF;ADaM;EACE,aAAA;EACA,iBAAA;ACXR;ADcM;EACE,oBAAA;EACA,QAAA;EACA,gBAAA;ACZR;ADcQ;EACE,gBAAA;EACA,0BAAA;EACA,eAAA;ACZV;ADiBI;EACE,OAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,YAAA;ACfN;ADiBM;EAPF;IAQI,kBAAA;IACA,MAAA;IACA,OAAA;IACA,YAAA;IACA,YAAA;IACA,UAAA;IACA,YAAA;IACA,WAAA;ECdN;AACF;ADgBM;EACE,YAAA;EACA,WAAA;EACA,oBAAA;KAAA,iBAAA;EACA,cAAA;ACdR;;ADuBA;EACE,WAAA;EACA,kBAAA;ACpBF;ADsBE;EACE,sGAAA;EAEA,kBAAA;EACA,kCAAA;EACA,sBAAA;ACrBJ;ADwBE;EACE,6GAAA;EAEA,kBAAA;EACA,kCAAA;EACA,sBAAA;ACvBJ;AD0BE;EACE,qGAAA;EAEA,+BAAA;EACA,sBAAA;EACA,kBAAA;ACzBJ;;AD8BA;EACE,yBAAA;EACA,WAAA;EACA,aAAA;EACA,iBAAA;EACA,eAAA;EACA,gBAAA;AC3BF;AD6BE;EACE,YAAA;EACA,eAAA;AC3BJ;AD8BE;EACE,gBAAA;AC5BJ;;ADkCE;EACE,iBAAA;EACA,cAAA;AC/BJ;ADkCE;EACE,sBAAA;EACA,gBAAA;EACA,qDAAA;AChCJ;ADkCI;EACE,eAAA;EACA,kBAAA;AChCN;ADkCM;EACE,YAAA;EACA,aAAA;EACA,kBAAA;EACA,yCAAA;EACA,oBAAA;KAAA,iBAAA;EACA,cAAA;AChCR;ADkCQ;EACE,+BAAA;KAAA,4BAAA;AChCV;ADmCQ;EACE,iCAAA;KAAA,8BAAA;ACjCV;ADoCQ;EACE,iCAAA;KAAA,8BAAA;AClCV;ADuCI;EACE,aAAA;EACA,kBAAA;ACrCN;;AD4CA;EACE,iCAAA;ACzCF;AD2CE;EACE,gBAAA;ACzCJ;AD4CE;EACE,gBAAA;AC1CJ;;AD+CA;EACE,eAAA;AC5CF;;AD+CA;EACE,yBAAA;EACA,sBAAA;EACA,yCAAA;EACA,aAAA;EACA,kBAAA;EACA,qDAAA;EACA,YAAA;EACA,gBAAA;AC5CF;;AD+CA;EACE,iBAAA;EACA,mBAAA;EACA,cAAA;AC5CF;;AD+CA;EACE,iBAAA;EACA,gBAAA;EACA,qBAAA;EACA,cAAA;AC5CF;;AD+CA;EACE,eAAA;EAEA,gBAAA;AC7CF;;ADgDA,2BAAA;AACA;EACE;IACE,qBAAA;EC7CF;EDgDA;IACE,eAAA;EC9CF;EDiDA;IACE,iBAAA;EC/CF;AACF;AD2DA;EACE,aAAA;EACA,YAAA;EACA,kBAAA;EACA,gBAAA;EACA,aAAA;EACA,uBAAA;EACA,mBAAA;EACA,kBAAA;ACzDF;AD2DE;EAVF;IAWI,YAAA;IACA,aAAA;ECxDF;AACF;AD0DE;EAfF;IAgBI,YAAA;IACA,aAAA;ECvDF;AACF;ADyDE;EACE,WAAA;EACA,kBAAA;EACA,MAAA;EACA,OAAA;EACA,WAAA;EACA,YAAA;EACA,kBAAA;EAGA,gDAAA;EACA,UAAA;EACA,oBAAA;ACvDJ;AD2DE;EACE,oBAAA;KAAA,iBAAA;EACA,yBAAA;KAAA,sBAAA;EACA,WAAA;EACA,YAAA;ACzDJ;;AD8DE;EACE,iBAAA;EACA,gBAAA;AC3DJ;AD8DE;EACE,iBAAA;EACA,gBAAA;AC5DJ;AD+DE;EACE,iBAAA;EACA,gBAAA;EACA,gBAAA;AC7DJ;ADgEE;EACE,gBAAA;AC9DJ;;ADkEA;EACE,UAAA;EACA,2BAAA;EACA,0DAAA;AC/DF;;ADkEA;EACE,UAAA;EACA,wBAAA;AC/DF;;ADkEA;EACE,WAAA;EACA,2BAAA;EAEA,2BAAA;EACA,4BAAA;EACA,sBAAA;EACA,aAAA;EACA,uBAAA;EACA,mBAAA;AChEF;ADkEE;EAXF;IAYI,YAAA;IACA,iBAAA;EC/DF;AACF;ADsEM;EAFF;IAGI,aAAA;IACA,8BAAA;ECnEN;AACF;ADuEE;EACE,mBAAA;EACA,mDAAA;EAEA,aAAA;EAGA,WAAA;EACA,mBAAA;ACxEJ;AD2EI;EAXF;IAYI,sBAAA;IACA,YAAA;ECxEJ;AACF;AD0EI;EACE,sBAAA;EACA,iBAAA;EAEA,SAAA;EACA,cAAA;EACA,aAAA;EACA,mBAAA;EACA,0CAAA;EACA,mBAAA;ACzEN;AD2EM;EAXF;IAYI,UAAA;IACA,eAAA;IACA,mBAAA;IACA,yCAAA;ECxEN;AACF;AD0EM;EACE,kBAAA;EACA,WAAA;ACxER;AD4EI;EACE,SAAA;EACA,cAAA;EACA,kBAAA;AC1EN;AD4EM;EALF;IAMI,UAAA;IACA,eAAA;ECzEN;AACF;AD2EM;EACE,aAAA;EACA,mBAAA;EACA,uBAAA;ACzER;AD2EQ;EACE,YAAA;ACzEV;AD6EM;EACE,gBAAA;AC3ER;;ADiFA;EACE;IACE,UAAA;IACA,eAAA;IACA,gBAAA;EC9EF;AACF;ADiFA;EACE;IACE,2BAAA;IACA,UAAA;EC/EF;EDkFA;IACE,wBAAA;IACA,UAAA;EChFF;AACF","file":"theme.css"}
|
||||
604
rss/css/theme.scss
Normal file
604
rss/css/theme.scss
Normal file
@@ -0,0 +1,604 @@
|
||||
@import 'variables';
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0.7rem 2rem;
|
||||
background-color: #ffffff;
|
||||
align-items: center;
|
||||
box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.12);
|
||||
position: relative;
|
||||
z-index: 999;
|
||||
font-family: "Nunito", sans-serif;
|
||||
background-color: #fff;
|
||||
|
||||
@media(max-width:768px) {
|
||||
// height: 48px;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 100px;
|
||||
display: flex;
|
||||
z-index: 999;
|
||||
cursor: pointer;
|
||||
background-color: #fff;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
nav {
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: -1;
|
||||
|
||||
@media(max-width:768px) {
|
||||
top: -200%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
|
||||
&.open {
|
||||
top: 99%;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
transition: all 0.3s;
|
||||
// padding: 5px 10px;
|
||||
background-color: #fff;
|
||||
height: auto;
|
||||
z-index: -1;
|
||||
opacity: 1;
|
||||
transition: all 0.3s;
|
||||
box-shadow: 0px 3px 5px 0px rgba(0, 0, 0, 0.12);
|
||||
|
||||
a {
|
||||
padding: 7px 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
color: #000000;
|
||||
font-weight: 500;
|
||||
margin-right: 15px;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
transition: color 0.3s;
|
||||
font-size: 16px;
|
||||
|
||||
&.active {
|
||||
font-weight: bold;
|
||||
// color: #your-brand-color;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 120%;
|
||||
left: 0;
|
||||
height: 2px;
|
||||
width: 100%;
|
||||
background: #{map-get($colors, primary)};
|
||||
transform: scaleX(0);
|
||||
transform-origin: left;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: #{map-get($colors, primary)};
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:hover::after {
|
||||
transform: scaleX(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-toggler {
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
font-size: 18px;
|
||||
|
||||
@media(min-width: 769px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.home-banner {
|
||||
height: calc(100vh - 55px);
|
||||
padding: 50px 0;
|
||||
display: flex;
|
||||
background-color: #{map-get($colors, white)};
|
||||
opacity: 0;
|
||||
animation: slideUp 1s ease-in-out forwards;
|
||||
// animation-delay: 0.2s;
|
||||
|
||||
&.no-height{
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
@media(max-width:768px) {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.home-banner-inner {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
|
||||
.container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.inner-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
height: 100%;
|
||||
|
||||
@media(max-width:768px) {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.image-col {
|
||||
flex: 0 0 45%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
border-radius: 12px;
|
||||
box-shadow: #{map-get($shadows, 3)};
|
||||
}
|
||||
}
|
||||
|
||||
.text-col {
|
||||
flex: 0 0 40%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
|
||||
@media(max-width: 768px) {
|
||||
flex: 0 0 100%;
|
||||
max-width: 100%;
|
||||
text-align: center;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 800;
|
||||
font-size: 3.7rem;
|
||||
line-height: 1.2;
|
||||
font-family: $font-family-base;
|
||||
|
||||
@media(max-width:768px) {
|
||||
font-size: 3.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 400;
|
||||
|
||||
@media(max-width:768px) {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.cta-btn {
|
||||
padding: 10px;
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.login-text {
|
||||
display: inline-flex;
|
||||
gap: 5px;
|
||||
font-weight: 400;
|
||||
|
||||
.login-link {
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.image-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
|
||||
@media(max-width: 768px) {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
height: 100%;
|
||||
z-index: 1;
|
||||
opacity: 0.1;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Banners
|
||||
.display-banner {
|
||||
width: 100%;
|
||||
padding: 120px 0px;
|
||||
|
||||
&.wedding-banner {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.8)),
|
||||
url('/rss/img/wedding.jpg');
|
||||
padding: 180px 0px;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
&.kissing-banner {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.8)),
|
||||
url('/rss/img/couple-kissing.jpg');
|
||||
padding: 180px 0px;
|
||||
background-position: center center;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
&.moving-banner {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.8)),
|
||||
url('/rss/img/couple.jpg');
|
||||
background-position: center 40%;
|
||||
background-size: cover;
|
||||
padding: 180px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
// Footer
|
||||
footer {
|
||||
background-color: #{map-get($colors, 'dark')};
|
||||
color: #fff;
|
||||
display: flex;
|
||||
padding: 25px 0px;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
|
||||
.logo {
|
||||
width: 125px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
|
||||
.features-section {
|
||||
|
||||
h2 {
|
||||
font-size: map-get($font-sizes, xl);
|
||||
color: map-get($colors, dark);
|
||||
}
|
||||
|
||||
.theme-card {
|
||||
border-radius: map-get($border-radius-sizes, md);
|
||||
overflow: hidden;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
|
||||
.card-header {
|
||||
padding: #{map-get($spacing, "2")};
|
||||
text-align: center;
|
||||
|
||||
img {
|
||||
width: 125px;
|
||||
height: 125px;
|
||||
border-radius: 50%;
|
||||
box-shadow: map-get($shadows, 2);
|
||||
object-fit: cover;
|
||||
margin: 0 auto;
|
||||
|
||||
&.object-left {
|
||||
object-position: left center;
|
||||
}
|
||||
|
||||
&.object-center {
|
||||
object-position: center center;
|
||||
}
|
||||
|
||||
&.object-bottom {
|
||||
object-position: bottom center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: #{map-get($spacing, "3")};
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix before release
|
||||
// Theme fonts
|
||||
.styled-title {
|
||||
font-family: "Nunito", sans-serif;
|
||||
|
||||
&.fw-900 {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
&.fw-600 {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.filter-section {
|
||||
padding: 2rem 0;
|
||||
}
|
||||
|
||||
.filter-card {
|
||||
background-color: #{map-get($colors, white)};
|
||||
border-radius: map-get($border-radius-sizes, md);
|
||||
box-shadow: map-get($shadows, 2);
|
||||
padding: #{map-get($spacing, "3")};
|
||||
text-align: center;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
height: 100%;
|
||||
margin: 15px 0px;
|
||||
}
|
||||
|
||||
.filter-icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #{map-get($colors, primary)};
|
||||
}
|
||||
|
||||
.filter-title {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
color: map-get($colors, dark);
|
||||
}
|
||||
|
||||
.filter-text {
|
||||
font-size: 1rem;
|
||||
color: map-get($colors, gray);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* Responsive adjustments */
|
||||
@media (max-width: 768px) {
|
||||
.filter-card {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.filter-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.filter-title {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Images
|
||||
|
||||
// Rounded col images
|
||||
.rounded-col-image {
|
||||
height: 450px;
|
||||
width: 450px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
@media(max-width:1200px) {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
@media(max-width:920px) {
|
||||
width: 225px;
|
||||
height: 225px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
-webkit-box-shadow: inset 0px 0px 27px -5px rgba(2, 2, 2, 1);
|
||||
-moz-box-shadow: inset 0px 0px 27px -5px rgba(2, 2, 2, 1);
|
||||
box-shadow: inset 0px 0px 27px -5px rgba(2, 2, 2, 1);
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
object-position: right;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
h2 {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.bolder {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.section-animation {
|
||||
opacity: 0;
|
||||
transform: translateY(50px);
|
||||
transition: opacity 0.8s ease-out, transform 0.8s ease-out;
|
||||
}
|
||||
|
||||
.section-animation.visible {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.user-container {
|
||||
width: 100%;
|
||||
height: calc(100vh - 110px);
|
||||
// background-image: url('/rss/img/happy-kissing.jpg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
@media(max-width: 768px) {
|
||||
height: auto;
|
||||
padding: 20px 0px;
|
||||
}
|
||||
|
||||
&.alt {
|
||||
// background-image: url('/rss/img/happy-kissing-2.jpg') !important;
|
||||
|
||||
.user-card {
|
||||
|
||||
@media(max-width: 768px) {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user-card {
|
||||
border-radius: 12px;
|
||||
background-color: #ffffffe7;
|
||||
// box-shadow: #{map-get($shadows, 3)};
|
||||
display: flex;
|
||||
// min-height: 60vh;
|
||||
// height: 60vh;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
// overflow: hidden;
|
||||
|
||||
@media(max-width: 768px) {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.form {
|
||||
background-color: #FFF;
|
||||
padding: 20px 0px;
|
||||
// height: 100%;
|
||||
flex: 40%;
|
||||
max-width: 40%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
box-shadow: #{map-get($shadows, 3)};
|
||||
border-radius: 12px;
|
||||
|
||||
@media(max-width: 768px) {
|
||||
flex: 100%;
|
||||
max-width: 100%;
|
||||
border-radius: 12px;
|
||||
box-shadow: #{map-get($shadows, 2)};
|
||||
}
|
||||
|
||||
.user-form {
|
||||
padding: 10px 15px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.text-col {
|
||||
flex: 60%;
|
||||
max-width: 60%;
|
||||
padding: 30px 20px;
|
||||
|
||||
@media(max-width: 768px) {
|
||||
flex: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.logo {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
|
||||
img {
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
|
||||
.text {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.section-animation {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(50px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
3
rss/css/variables.css
Normal file
3
rss/css/variables.css
Normal file
@@ -0,0 +1,3 @@
|
||||
/* ========================================================================== */
|
||||
/* Flexbox Maps */
|
||||
/* ========================================================================== *//*# sourceMappingURL=variables.css.map */
|
||||
1
rss/css/variables.css.map
Normal file
1
rss/css/variables.css.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sources":["variables.scss"],"names":[],"mappings":"AAwHA,+EAAA;AACA,+EAAA;AACA,+EAAA","file":"variables.css"}
|
||||
348
rss/css/variables.scss
Normal file
348
rss/css/variables.scss
Normal file
@@ -0,0 +1,348 @@
|
||||
$font-family-base: "Nunito", sans-serif;
|
||||
$font-family-mono: Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
|
||||
$font-sizes: (
|
||||
xs: 0.75rem,
|
||||
sm: 0.875rem,
|
||||
base: 1.1rem,
|
||||
lg: 1.35rem,
|
||||
xl: 1.8rem,
|
||||
xxl: 2.2rem
|
||||
);
|
||||
|
||||
$font-weights: (
|
||||
light: 300,
|
||||
normal: 400,
|
||||
medium: 500,
|
||||
semibold: 600,
|
||||
bold: 700
|
||||
);
|
||||
|
||||
$line-heights: (
|
||||
tight: 1,
|
||||
normal: 1.3,
|
||||
heading: 1.5
|
||||
);
|
||||
|
||||
$colors: (
|
||||
primary: #aa0b3d,
|
||||
primary-alt: #aa0b3da8,
|
||||
alt: #b5451896,
|
||||
secondary: #333333,
|
||||
success: #34b97b,
|
||||
warning: #ffc107,
|
||||
danger: #dc3545,
|
||||
info: #0dcaf0,
|
||||
light: #e5e5e5,
|
||||
dark: #212529,
|
||||
white: #ffffff,
|
||||
black: #000000,
|
||||
muted: #c7c7c7,
|
||||
gray: #6c757d,
|
||||
accent-white: #f7f7f7,
|
||||
accent: #08bbbb,
|
||||
transparent: transparent
|
||||
);
|
||||
|
||||
$space: (
|
||||
0: 0,
|
||||
1: 0.25rem,
|
||||
2: 0.5rem,
|
||||
3: 1rem,
|
||||
4: 1.5rem,
|
||||
5: 3rem
|
||||
);
|
||||
|
||||
$radii: (
|
||||
none: 0,
|
||||
sm: 0.125rem,
|
||||
md: 0.25rem,
|
||||
lg: 0.5rem,
|
||||
xl: 1rem,
|
||||
full: 9999px
|
||||
);
|
||||
|
||||
$border-widths: (
|
||||
0: 0,
|
||||
1: 1px,
|
||||
2: 2px,
|
||||
3: 3px
|
||||
);
|
||||
|
||||
$shadows: (
|
||||
1: 0 1px 3px rgba(0, 0, 0, 0.12),
|
||||
2: 0 2px 6px rgba(0, 0, 0, 0.16),
|
||||
3: 0 4px 10px rgba(0, 0, 0, 0.18),
|
||||
4: 0 6px 15px rgba(0, 0, 0, 0.20),
|
||||
5: 0 10px 24px rgba(0, 0, 0, 0.22)
|
||||
);
|
||||
|
||||
$drop-shadows: (
|
||||
1: 0 2px 4px rgba(0, 0, 0, 0.14),
|
||||
2: 0 4px 8px rgba(0, 0, 0, 0.18),
|
||||
3: 0 8px 16px rgba(0, 0, 0, 0.20),
|
||||
4: 0 12px 24px rgba(0, 0, 0, 0.22),
|
||||
5: 0 20px 40px rgba(0, 0, 0, 0.24)
|
||||
);
|
||||
|
||||
$timings: (
|
||||
fast: 0.15s,
|
||||
base: 0.25s,
|
||||
slow: 0.4s
|
||||
);
|
||||
|
||||
$easings: (
|
||||
in-out: cubic-bezier(.4, 0, .2, 1),
|
||||
out: cubic-bezier(0, 0, .2, 1),
|
||||
in: cubic-bezier(.4, 0, 1, 1)
|
||||
);
|
||||
|
||||
$grid-columns: 12;
|
||||
$container-padding: 1rem;
|
||||
|
||||
$breakpoints: (
|
||||
sm: 576px,
|
||||
md: 768px,
|
||||
lg: 992px,
|
||||
xl: 1200px,
|
||||
xxl: 1400px,
|
||||
xxxl: 1600px
|
||||
);
|
||||
|
||||
$container-max-widths: (
|
||||
sm: 540px,
|
||||
md: 720px,
|
||||
lg: 960px,
|
||||
xl: 1140px,
|
||||
xxl: 1320px,
|
||||
xxxl: 1520px
|
||||
);
|
||||
|
||||
/* ========================================================================== */
|
||||
/* Flexbox Maps */
|
||||
/* ========================================================================== */
|
||||
$flex-directions: (
|
||||
row: row,
|
||||
row-reverse: row-reverse,
|
||||
column: column,
|
||||
column-reverse: column-reverse
|
||||
);
|
||||
|
||||
$justify-content-values: (
|
||||
start: flex-start,
|
||||
end: flex-end,
|
||||
center: center,
|
||||
between: space-between,
|
||||
around: space-around,
|
||||
evenly: space-evenly
|
||||
);
|
||||
|
||||
$align-items-values: (
|
||||
start: flex-start,
|
||||
end: flex-end,
|
||||
center: center,
|
||||
baseline: baseline,
|
||||
stretch: stretch
|
||||
);
|
||||
|
||||
$align-content-values: (
|
||||
start: flex-start,
|
||||
end: flex-end,
|
||||
center: center,
|
||||
between: space-between,
|
||||
around: space-around,
|
||||
stretch: stretch
|
||||
);
|
||||
|
||||
|
||||
$gutters: (
|
||||
0: map-get($space, 0),
|
||||
1: map-get($space, 1),
|
||||
2: map-get($space, 2),
|
||||
3: map-get($space, 3),
|
||||
4: map-get($space, 4),
|
||||
5: map-get($space, 5)
|
||||
);
|
||||
|
||||
$display-types: block, flex, inline, inline-block, inline-flex, grid, none;
|
||||
|
||||
$z: (
|
||||
base: 1,
|
||||
dropdown: 1000,
|
||||
overlay: 1100,
|
||||
modal: 1200,
|
||||
toast: 1300
|
||||
);
|
||||
|
||||
$control-colors: (
|
||||
bg: #fdfdfd,
|
||||
fg: map-get($colors, secondary),
|
||||
border: map-get($colors, muted),
|
||||
border-hover: map-get($colors, secondary),
|
||||
focus: map-get($colors, primary),
|
||||
ring: rgba(map-get($colors, primary), 0.2),
|
||||
placeholder: map-get($colors, gray),
|
||||
disabled-bg: map-get($colors, light)
|
||||
);
|
||||
|
||||
$control-sizes: (
|
||||
sm: (
|
||||
font-size: map-get($font-sizes, sm),
|
||||
line-height: map-get($line-heights, normal),
|
||||
px: 0.5rem,
|
||||
py: 0.25rem,
|
||||
radius: map-get($radii, md)
|
||||
),
|
||||
md: (
|
||||
font-size: map-get($font-sizes, base),
|
||||
line-height: map-get($line-heights, normal),
|
||||
px: 0.75rem,
|
||||
py: 0.375rem,
|
||||
radius: map-get($radii, md)
|
||||
),
|
||||
lg: (
|
||||
font-size: map-get($font-sizes, lg),
|
||||
line-height: map-get($line-heights, normal),
|
||||
px: 1rem,
|
||||
py: 0.5rem,
|
||||
radius: map-get($radii, lg)
|
||||
),
|
||||
xl: (
|
||||
font-size: map-get($font-sizes, xl),
|
||||
line-height: map-get($line-heights, heading),
|
||||
px: 1.25rem,
|
||||
py: 0.75rem,
|
||||
radius: map-get($radii, lg)
|
||||
)
|
||||
);
|
||||
|
||||
$control-border: (
|
||||
width: map-get($border-widths, 1),
|
||||
style: solid
|
||||
);
|
||||
|
||||
$size-px: (
|
||||
0: 0px,
|
||||
1: 1px,
|
||||
2: 2px,
|
||||
4: 4px,
|
||||
8: 8px,
|
||||
12: 12px,
|
||||
16: 16px,
|
||||
20: 20px,
|
||||
24: 24px,
|
||||
32: 32px,
|
||||
40: 40px,
|
||||
48: 48px,
|
||||
56: 56px,
|
||||
64: 64px,
|
||||
80: 80px,
|
||||
96: 96px,
|
||||
112: 112px,
|
||||
128: 128px,
|
||||
160: 160px,
|
||||
192: 192px,
|
||||
224: 224px,
|
||||
256: 256px,
|
||||
320: 320px,
|
||||
384: 384px,
|
||||
448: 448px,
|
||||
512: 512px
|
||||
);
|
||||
|
||||
$size-percent: (
|
||||
0: 0%,
|
||||
25: 25%,
|
||||
33: 33.3333%,
|
||||
50: 50%,
|
||||
66: 66.6667%,
|
||||
75: 75%,
|
||||
100: 100%
|
||||
);
|
||||
|
||||
$size-rem: (
|
||||
0: 0,
|
||||
1: 0.25rem,
|
||||
2: 0.5rem,
|
||||
3: 1rem,
|
||||
4: 1.5rem,
|
||||
5: 2rem,
|
||||
6: 3rem,
|
||||
7: 4rem,
|
||||
8: 6rem
|
||||
);
|
||||
|
||||
$size-vw: (
|
||||
10: 10vw,
|
||||
20: 20vw,
|
||||
25: 25vw,
|
||||
33: 33vw,
|
||||
40: 40vw,
|
||||
50: 50vw,
|
||||
60: 60vw,
|
||||
66: 66vw,
|
||||
75: 75vw,
|
||||
80: 80vw,
|
||||
90: 90vw,
|
||||
100: 100vw
|
||||
);
|
||||
|
||||
$size-vh: (
|
||||
10: 10vh,
|
||||
20: 20vh,
|
||||
25: 25vh,
|
||||
33: 33vh,
|
||||
40: 40vh,
|
||||
50: 50vh,
|
||||
60: 60vh,
|
||||
66: 66vh,
|
||||
75: 75vh,
|
||||
80: 80vh,
|
||||
90: 90vh,
|
||||
100: 100vh
|
||||
);
|
||||
|
||||
$size-dvh: (
|
||||
10: 10dvh,
|
||||
20: 20dvh,
|
||||
25: 25dvh,
|
||||
33: 33dvh,
|
||||
40: 40dvh,
|
||||
50: 50dvh,
|
||||
60: 60dvh,
|
||||
66: 66dvh,
|
||||
75: 75dvh,
|
||||
80: 80dvh,
|
||||
90: 90dvh,
|
||||
100: 100dvh
|
||||
);
|
||||
|
||||
$size-svh: (
|
||||
10: 10svh,
|
||||
20: 20svh,
|
||||
25: 25svh,
|
||||
33: 33svh,
|
||||
40: 40svh,
|
||||
50: 50svh,
|
||||
60: 60svh,
|
||||
66: 66svh,
|
||||
75: 75svh,
|
||||
80: 80svh,
|
||||
90: 90svh,
|
||||
100: 100svh
|
||||
);
|
||||
|
||||
$size-lvh: (
|
||||
10: 10lvh,
|
||||
20: 20lvh,
|
||||
25: 25lvh,
|
||||
33: 33lvh,
|
||||
40: 40lvh,
|
||||
50: 50lvh,
|
||||
60: 60lvh,
|
||||
66: 66lvh,
|
||||
75: 75lvh,
|
||||
80: 80lvh,
|
||||
90: 90lvh,
|
||||
100: 100lvh
|
||||
);
|
||||
261
rss/js/main.js
Normal file
261
rss/js/main.js
Normal file
@@ -0,0 +1,261 @@
|
||||
// ==========================================================================
|
||||
// HTTP helpers
|
||||
// ==========================================================================
|
||||
async function _post(formObj, target, func) {
|
||||
try {
|
||||
const body = await createFormData(formObj, target, func);
|
||||
const res = await fetch('/rss/php/handler.php', { method: 'POST', body });
|
||||
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
return await res.json();
|
||||
} catch (err) {
|
||||
console.error('Post failed', err);
|
||||
return { ok: false, error: String(err?.message || err) };
|
||||
}
|
||||
}
|
||||
|
||||
async function createFormData(data = {}, target, func) {
|
||||
const fd = new FormData();
|
||||
try {
|
||||
Object.entries(data || {}).forEach(([k, v]) => {
|
||||
if (Array.isArray(v)) v.forEach((item) => fd.append(`${k}[]`, item));
|
||||
else if (v !== undefined && v !== null) fd.append(k, v);
|
||||
});
|
||||
if (target !== undefined) fd.append('target', target);
|
||||
if (func !== undefined) fd.append('function', func);
|
||||
} catch (err) {
|
||||
console.error('Incorrect use of object', err);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// DOM helpers
|
||||
// ==========================================================================
|
||||
function createEl(tag, classArr = null, text = null, dataAttrs = null) {
|
||||
const el = document.createElement(tag);
|
||||
if (text !== null && text !== undefined) el.append(document.createTextNode(String(text)));
|
||||
if (classArr && classArr.length) el.classList.add(...classArr);
|
||||
if (dataAttrs && Object.keys(dataAttrs).length) {
|
||||
for (const [k, v] of Object.entries(dataAttrs)) el.dataset[k] = v;
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
function getRandomID() {
|
||||
if (crypto?.randomUUID) return crypto.randomUUID();
|
||||
return Math.random().toString(36).slice(2);
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// Alerts (aligned with .alert + .alert-{name})
|
||||
// ==========================================================================
|
||||
/*
|
||||
generateAlert('success', 'Saved!') → <div class="alert alert-success" ...>Saved!</div>
|
||||
Options: { dismissAfter: ms, withClose: boolean, role: 'status'|'alert' }
|
||||
*/
|
||||
async function generateAlert(type = 'info', text = '', opts = {}) {
|
||||
const { dismissAfter = 0, withClose = false, role = 'status' } = opts;
|
||||
const alert = createEl('div', ['alert', `alert-${type}`]);
|
||||
alert.setAttribute('role', role); // status (polite) or alert (assertive)
|
||||
alert.setAttribute('aria-live', role === 'alert' ? 'assertive' : 'polite');
|
||||
alert.append(document.createTextNode(text));
|
||||
|
||||
if (withClose) {
|
||||
const btn = createEl('button', ['btn', 'btn-sm', 'btn-ghost-dark'], '×', { dismiss: 'alert' });
|
||||
btn.setAttribute('aria-label', 'Close alert');
|
||||
btn.style.marginLeft = '0.5rem';
|
||||
btn.addEventListener('click', () => alert.remove());
|
||||
alert.append(btn);
|
||||
}
|
||||
|
||||
if (dismissAfter > 0) setTimeout(() => alert.remove(), dismissAfter);
|
||||
return alert;
|
||||
}
|
||||
|
||||
async function removeAlert(scope = document) {
|
||||
scope.querySelectorAll('.alert').forEach((a) => a.remove());
|
||||
}
|
||||
|
||||
// ==========================================================================
|
||||
// On load
|
||||
// ==========================================================================
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Menu toggler (pointerdown → click for better a11y; add ARIA state)
|
||||
const toggler = document.getElementById('menu-toggler');
|
||||
const menu = document.getElementById('mainMenu');
|
||||
|
||||
if (toggler && menu) {
|
||||
toggler.setAttribute('aria-expanded', 'false');
|
||||
toggler.setAttribute('aria-controls', 'mainMenu');
|
||||
|
||||
const toggleMenu = () => {
|
||||
const isOpen = menu.classList.toggle('open');
|
||||
toggler.setAttribute('aria-expanded', String(isOpen));
|
||||
};
|
||||
|
||||
toggler.addEventListener('click', toggleMenu);
|
||||
toggler.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); toggleMenu(); }
|
||||
});
|
||||
}
|
||||
|
||||
// Section reveal
|
||||
const sections = document.querySelectorAll('section');
|
||||
const observer = new IntersectionObserver(
|
||||
(entries, obs) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('visible');
|
||||
obs.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 0.05, rootMargin: '0px 0px -50px 0px' }
|
||||
);
|
||||
|
||||
sections.forEach((section) => {
|
||||
section.classList.add('section-animation');
|
||||
observer.observe(section);
|
||||
});
|
||||
|
||||
(() => {
|
||||
function resolveTarget(el) {
|
||||
const viaData = el.getAttribute('data-target');
|
||||
const viaHref = el.getAttribute('href');
|
||||
if (viaData) return document.querySelector(viaData);
|
||||
if (viaHref && viaHref.startsWith('#')) return document.querySelector(viaHref);
|
||||
// Fallback: if only one modal exists and no selector provided
|
||||
const all = document.querySelectorAll('.modal');
|
||||
return all.length === 1 ? all[0] : null;
|
||||
}
|
||||
|
||||
function trapFocus(modal, e) {
|
||||
const focusables = modal.querySelectorAll('a,button,input,textarea,select,[tabindex]:not([tabindex="-1"])');
|
||||
if (!focusables.length) return;
|
||||
const first = focusables[0], last = focusables[focusables.length - 1];
|
||||
if (e.shiftKey && document.activeElement === first) { last.focus(); e.preventDefault(); }
|
||||
else if (!e.shiftKey && document.activeElement === last) { first.focus(); e.preventDefault(); }
|
||||
}
|
||||
|
||||
let lastActive = null;
|
||||
function openModal(modal) {
|
||||
if (!modal) return;
|
||||
lastActive = document.activeElement;
|
||||
modal.classList.add('open');
|
||||
modal.setAttribute('aria-hidden', 'false');
|
||||
(modal.querySelector('.modal__dialog') || modal).focus({ preventScroll: true });
|
||||
modal.addEventListener('keydown', onKeyDown);
|
||||
}
|
||||
function closeModal(modal) {
|
||||
if (!modal) return;
|
||||
modal.classList.remove('open');
|
||||
modal.setAttribute('aria-hidden', 'true');
|
||||
modal.removeEventListener('keydown', onKeyDown);
|
||||
if (lastActive) lastActive.focus({ preventScroll: true });
|
||||
}
|
||||
function onKeyDown(e) {
|
||||
const modal = e.currentTarget;
|
||||
if (e.key === 'Escape') closeModal(modal);
|
||||
if (e.key === 'Tab') trapFocus(modal, e);
|
||||
}
|
||||
|
||||
// Openers
|
||||
document.querySelectorAll('.modal-open').forEach(btn => {
|
||||
btn.addEventListener('click', (e) => {
|
||||
const modal = resolveTarget(btn);
|
||||
if (modal) openModal(modal);
|
||||
e.preventDefault();
|
||||
});
|
||||
btn.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); btn.click(); }
|
||||
});
|
||||
});
|
||||
|
||||
// Closers (overlay or close button)
|
||||
document.addEventListener('click', (e) => {
|
||||
const overlay = e.target.closest('.modal__overlay');
|
||||
const closeBtn = e.target.closest('.modal__close');
|
||||
if (!overlay && !closeBtn) return;
|
||||
const modal = e.target.closest('.modal');
|
||||
if (modal) closeModal(modal);
|
||||
});
|
||||
})();
|
||||
|
||||
(() => {
|
||||
function getTargetId(tab) {
|
||||
const href = tab.getAttribute('href');
|
||||
if (href && href.startsWith('#')) return href.slice(1);
|
||||
const aria = tab.getAttribute('aria-controls');
|
||||
if (aria) return aria;
|
||||
return null;
|
||||
}
|
||||
|
||||
document.querySelectorAll('.tabs').forEach(tabs => {
|
||||
const tabEls = tabs.querySelectorAll('.tab');
|
||||
if (!tabEls.length) return;
|
||||
|
||||
// If panels are used, they should be siblings elsewhere with .tab-panel
|
||||
function activateTab(tab) {
|
||||
// Toggle active tab
|
||||
tabEls.forEach(t => t.classList.toggle('active', t === tab));
|
||||
|
||||
// Panels (optional)
|
||||
const targetId = getTargetId(tab);
|
||||
const panels = document.querySelectorAll('.tab-panel');
|
||||
if (panels.length) {
|
||||
panels.forEach(p => {
|
||||
const match = p.id && targetId && p.id === targetId;
|
||||
p.hidden = !match;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Initial state: keep existing .active or default to first
|
||||
const current = tabs.querySelector('.tab.active') || tabEls[0];
|
||||
if (current) activateTab(current);
|
||||
|
||||
tabEls.forEach(tab => {
|
||||
tab.setAttribute('role', 'tab');
|
||||
tab.addEventListener('click', (e) => {
|
||||
activateTab(tab);
|
||||
if (tab.hasAttribute('href')) e.preventDefault(); // prevent jump for href="#id"
|
||||
});
|
||||
tab.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); tab.click(); }
|
||||
// Optional arrow key UX within the same .tabs group
|
||||
if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') {
|
||||
e.preventDefault();
|
||||
const arr = Array.from(tabEls);
|
||||
const i = arr.indexOf(tab);
|
||||
const next = e.key === 'ArrowRight' ? (i + 1) % arr.length : (i - 1 + arr.length) % arr.length;
|
||||
arr[next].focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
});
|
||||
|
||||
document.querySelectorAll('.accordion').forEach(acc => {
|
||||
const single = acc.classList.contains('accordion--single');
|
||||
|
||||
acc.querySelectorAll('.accordion__header').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
const item = btn.closest('.accordion__item');
|
||||
const isOpen = item.classList.contains('is-open');
|
||||
|
||||
if (single) {
|
||||
acc.querySelectorAll('.accordion__item.is-open').forEach(other => {
|
||||
if (other !== item) other.classList.remove('is-open');
|
||||
});
|
||||
}
|
||||
item.classList.toggle('is-open', !isOpen);
|
||||
});
|
||||
|
||||
btn.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); btn.click(); }
|
||||
});
|
||||
});
|
||||
});
|
||||
9
rss/json/pages/404.json
Normal file
9
rss/json/pages/404.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "TITLE",
|
||||
"long_desc": "DESC",
|
||||
"short_desc": "SHORT_DESC",
|
||||
"template": "404",
|
||||
"restricted": false,
|
||||
"redirect_login": false,
|
||||
"robots": "noindex"
|
||||
}
|
||||
8
rss/json/pages/index.json
Normal file
8
rss/json/pages/index.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"title": "Listings",
|
||||
"long_desc": "Testing the description!",
|
||||
"short_desc": "Test desc",
|
||||
"template": "listings",
|
||||
"restricted": false,
|
||||
"redirect_login": false
|
||||
}
|
||||
11
rss/php/autoload.php
Normal file
11
rss/php/autoload.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
spl_autoload_register(function ($class){
|
||||
$class = str_replace('App\\', '', $class);
|
||||
$class = str_replace('\\', '/', $class);
|
||||
|
||||
$file = $_SERVER['DOCUMENT_ROOT'] . '/rss/php/class/'.$class.'.php';
|
||||
|
||||
if(file_exists($file)){
|
||||
require_once($file);
|
||||
}
|
||||
});
|
||||
15
rss/php/class/Main.php
Normal file
15
rss/php/class/Main.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
namespace App;
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/autoload.php');
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Exception;
|
||||
|
||||
class Main extends Sys{
|
||||
|
||||
|
||||
public function __construct(){
|
||||
|
||||
}
|
||||
}
|
||||
34
rss/php/class/Sys.php
Normal file
34
rss/php/class/Sys.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace App;
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/autoload.php');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/conf.php');
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Exception;
|
||||
use stdClass;
|
||||
|
||||
class Sys{
|
||||
protected $conn;
|
||||
|
||||
public function __construct(){
|
||||
global $conn;
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
public function validateVar($val){
|
||||
if(isset($val) && !empty($val) && $val){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function createResponse($status, $message = null, $info = null){
|
||||
$resp = new stdClass();
|
||||
$resp->status = $status;
|
||||
$resp->message = $message;
|
||||
$resp->info = $info;
|
||||
return $resp;
|
||||
}
|
||||
}
|
||||
178
rss/php/class/Users.php
Normal file
178
rss/php/class/Users.php
Normal file
@@ -0,0 +1,178 @@
|
||||
<?php
|
||||
namespace App;
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/autoload.php');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/conf.php');
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
use Exception;
|
||||
|
||||
class Users extends Sys{
|
||||
public $id;
|
||||
public $fName;
|
||||
public $lName;
|
||||
public $email;
|
||||
public $cemail;
|
||||
public $psw;
|
||||
public $cpsw;
|
||||
private $loginState;
|
||||
|
||||
public function __construct(){
|
||||
global $conn;
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
public function login(){
|
||||
if($this->isAuth()){
|
||||
return $this->createResponse('success', 'Logged in');
|
||||
}else{
|
||||
if($this->validateVar($this->email) && $this->validateVar($this->psw)){
|
||||
$stmt = $this->conn->prepare('SELECT id, psw FROM users WHERE email = :email');
|
||||
$stmt->bindValue(':email', $this->email);
|
||||
$stmt->execute();
|
||||
if($user = $stmt->fetch(PDO::FETCH_ASSOC)){
|
||||
if(password_verify($this->psw, $user['psw'])){
|
||||
$auth_hash = bin2hex(random_bytes(16));
|
||||
$this->id = intval($user['id']);
|
||||
setcookie("user_hash", $auth_hash, [
|
||||
'expires' => time() + 2678400,
|
||||
'path' => '/',
|
||||
// 'secure' => true, // Enable on publish
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict',
|
||||
'domain' => $_SERVER['HTTP_HOST']
|
||||
]);
|
||||
|
||||
setcookie("usid", $this->id, [
|
||||
'expires' => time() + 2678400,
|
||||
'path' => '/',
|
||||
// 'secure' => true, // Enable on publish
|
||||
'httponly' => false,
|
||||
'samesite' => 'Strict',
|
||||
'domain' => $_SERVER['HTTP_HOST']
|
||||
]);
|
||||
|
||||
$stmt = $this->conn->prepare('INSERT INTO user_sessions (user_hash, user_id, login_ip) VALUES (:user_hash, :user_id, :login_ip)');
|
||||
$stmt->bindValue(':user_hash', $auth_hash);
|
||||
$stmt->bindValue(':user_id', $user['id']);
|
||||
$stmt->bindValue(':login_ip', $_SERVER['REMOTE_ADDR']);
|
||||
$stmt->execute();
|
||||
return $this->createResponse('success', 'Logged in');
|
||||
}else{
|
||||
return $this->createResponse('fail', 'Incorrect details');
|
||||
}
|
||||
}else{
|
||||
return $this->createResponse('fail', 'Incorrect details');
|
||||
}
|
||||
}else{
|
||||
return $this->createResponse('fail', 'Please fill out all the fields');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function logout(){
|
||||
if(!$this->isAuth()){
|
||||
return true;
|
||||
}
|
||||
|
||||
$stmt = $this->conn->prepare('DELETE FROM user_sessions WHERE user_hash = :user_hash AND user_id = :user_id AND login_ip = :login_ip');
|
||||
$stmt->bindValue(':user_hash', $_COOKIE['user_hash']);
|
||||
$stmt->bindValue(':user_id', intval($_COOKIE['usid']));
|
||||
$stmt->bindValue(':login_ip', $_SERVER['REMOTE_ADDR']);
|
||||
$stmt->execute();
|
||||
|
||||
if(isset($_SERVER['HTTP_COOKIE'])) {
|
||||
$cookies = explode(';', $_SERVER['HTTP_COOKIE']);
|
||||
foreach ($cookies as $cookie) {
|
||||
$parts = explode('=', $cookie);
|
||||
$name = trim($parts[0]);
|
||||
setcookie($name, '', time() - 1000);
|
||||
setcookie($name, '', time() - 1000, '/');
|
||||
setcookie($name, '', time() - 1000, '/', $_SERVER['HTTP_HOST']);
|
||||
}
|
||||
session_destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public function create_user(){
|
||||
// Validate inputs
|
||||
if(!$this->validateVar($this->fName)){
|
||||
return $this->createResponse('fail', 'Please enter your first name');
|
||||
}
|
||||
|
||||
if(!$this->validateVar($this->lName)){
|
||||
return $this->createResponse('fail', 'Please enter your last name');
|
||||
}
|
||||
|
||||
if(!$this->validateVar($this->email)){
|
||||
return $this->createResponse('fail', 'Please enter your email');
|
||||
}
|
||||
|
||||
if($this->email != $this->cemail){
|
||||
return $this->createResponse('fail', 'The email adresses do not match');
|
||||
}
|
||||
|
||||
if(!filter_var($this->email, FILTER_VALIDATE_EMAIL)){
|
||||
return $this->createResponse('fail', 'Please enter a valid email');
|
||||
}
|
||||
|
||||
if($this->psw != $this->cpsw){
|
||||
return $this->createResponse('fail', 'Please enter a valid password');
|
||||
}
|
||||
|
||||
if(strlen($this->psw) < 6){
|
||||
return $this->createResponse('fail', 'Your password needs to be at least 6 characters');
|
||||
}
|
||||
|
||||
$stm = $this->conn->prepare('SELECT * FROM users WHERE email = :email');
|
||||
$stm->bindValue(':email', $this->email);
|
||||
$stm->execute();
|
||||
|
||||
if($stm->rowCount()){
|
||||
return $this->createResponse('fail', 'Email already exists');
|
||||
}
|
||||
|
||||
$password = password_hash(trim($this->psw), PASSWORD_BCRYPT, array('cost' => 12));
|
||||
|
||||
$stmt = $this->conn->prepare('INSERT INTO users (fname, lname, email, psw) VALUES (:fname, :lname, :email, :psw)');
|
||||
$stmt->bindValue(':fname', trim($this->fName));
|
||||
$stmt->bindValue(':lname', trim($this->lName));
|
||||
$stmt->bindValue(':email', trim($this->email));
|
||||
$stmt->bindValue(':psw', $password);
|
||||
$stmt->execute();
|
||||
|
||||
if($stmt->rowCount()){
|
||||
return $this->createResponse('success', 'Account has been created');
|
||||
}else{
|
||||
return $this->createResponse('fail', 'Something went wrong. Please try again or contact support');
|
||||
}
|
||||
}
|
||||
|
||||
public function isAuth(){
|
||||
if(isset($_COOKIE['user_hash']) && !empty($_COOKIE['user_hash'])){
|
||||
$user_ip = $_SERVER['REMOTE_ADDR'];
|
||||
$user_hash = $_COOKIE['user_hash'];
|
||||
$user_id = intval($_COOKIE['usid']);
|
||||
$stmt = $this->conn->prepare('SELECT * FROM user_sessions WHERE user_hash = :user_hash AND user_id = :user_id AND login_ip = :user_ip');
|
||||
$stmt->bindValue(':user_hash', $user_hash);
|
||||
$stmt->bindValue(':user_id', $user_id);
|
||||
$stmt->bindValue(':user_ip', $user_ip);
|
||||
$stmt->execute();
|
||||
return $stmt->rowCount();
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getMyself(){
|
||||
if($this->isAuth()){
|
||||
$user_id = intval($_COOKIE['usid']);
|
||||
$stmt = $this->conn->prepare('SELECT * FROM users WHERE id = :id');
|
||||
$stmt->bindValue(':id', $user_id);
|
||||
$stmt->execute();
|
||||
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
unset($user['psw']);
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
36
rss/php/conf.php
Normal file
36
rss/php/conf.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
if(file_exists($_SERVER['DOCUMENT_ROOT'] . '/.env')){
|
||||
$lines = file($_SERVER['DOCUMENT_ROOT'] . '/.env', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
||||
|
||||
if(isset($lines) && !empty($lines)){
|
||||
foreach($lines as $line){
|
||||
if(strpos(trim($line), '#') === 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
list($name, $value) = explode('=', $line, 2);
|
||||
$name = trim($name);
|
||||
$value = trim($value);
|
||||
|
||||
$value = trim($value, '"\'');
|
||||
|
||||
putenv("$name=$value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
$db_user = getenv("DB_USER");
|
||||
$db_pass = getenv("DB_PASS");
|
||||
$db_server = getenv("DB_SERVER");
|
||||
$db_name = getenv("DB_NAME");
|
||||
|
||||
$conn = new PDO("mysql:host=$db_server;dbname=$db_name;charset=utf8mb4", $db_user, $db_pass);
|
||||
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
}catch(PDOException $e){
|
||||
var_dump($e);
|
||||
echo json_encode('Connection exception');
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
60
rss/php/handler.php
Normal file
60
rss/php/handler.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/autoload.php');
|
||||
use App\Main;
|
||||
use App\Users;
|
||||
|
||||
$classList = array(
|
||||
'Main' => Main::class,
|
||||
'Users' => Users::class
|
||||
);
|
||||
|
||||
session_start();
|
||||
|
||||
if(file_exists($_SERVER['DOCUMENT_ROOT'] . '/rss/php/conf.php')){
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/conf.php');
|
||||
}
|
||||
|
||||
if(isset($_POST['function']) && !empty($_POST['function']) && isset($_POST['target']) && !empty($_POST['target'])){
|
||||
$t_class = trim($_POST['target']);
|
||||
$t_func = trim($_POST['function']);
|
||||
if(array_key_exists($t_class, $classList) && class_exists($classList[$t_class])){
|
||||
if(method_exists($classList[$t_class], $t_func)){
|
||||
$_class = new $classList[$t_class];
|
||||
|
||||
if(count($_POST) > 2){
|
||||
foreach($_POST as $prop => $val){
|
||||
if(property_exists($classList[$t_class], $prop)){
|
||||
$_class->$prop = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($_FILES) && !empty($_FILES)){
|
||||
foreach($_FILES as $prop => $val){
|
||||
if(property_exists($classList[$t_class], $prop)){
|
||||
$_class->$prop = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$response = $_class->$t_func();
|
||||
if($response){
|
||||
$msg = $response;
|
||||
}else{
|
||||
$msg = new stdClass();
|
||||
$msg->status = 'fail';
|
||||
$msg->message = $response;
|
||||
}
|
||||
}else{
|
||||
$msg = new stdClass();
|
||||
$msg->status = 'fail';
|
||||
$msg->message = 'Invalid function';
|
||||
}
|
||||
}else{
|
||||
$msg = new stdClass();
|
||||
$msg->status = 'fail';
|
||||
$msg->message = 'Invalid class';
|
||||
}
|
||||
}
|
||||
|
||||
die(json_encode($msg));
|
||||
11
rss/php/includes/footer.php
Normal file
11
rss/php/includes/footer.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
global $pageInfo;
|
||||
if (file_exists($_SERVER['DOCUMENT_ROOT'] . '/rss/js/' . $pageInfo['template'] . '.js')) {
|
||||
echo '<script src="/rss/js/' . $pageInfo['template'] . '.js"></script>';
|
||||
}
|
||||
?>
|
||||
|
||||
<footer>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
79
rss/php/includes/header.php
Normal file
79
rss/php/includes/header.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
$page = isset($_GET['page']) ? basename($_GET['page']) : 'index';
|
||||
$jsonFile = $_SERVER['DOCUMENT_ROOT'] . "/rss/json/pages/{$page}.json";
|
||||
$pageData = file_exists($jsonFile) ? json_decode(file_get_contents($jsonFile), true) : [
|
||||
'title' => 'SITE',
|
||||
'long_desc' => '',
|
||||
'robots' => 'index, follow'
|
||||
];
|
||||
|
||||
// Set default values
|
||||
$siteUrl = 'https://DOMAIN.com';
|
||||
$pageUrl = $page === 'index' ? $siteUrl . '/' : $siteUrl . '/' . $page . '/';
|
||||
$title = $pageData['title'] ?? 'SITE';
|
||||
$description = $pageData['long_desc'] ?? '';
|
||||
$keywords = '';
|
||||
$robots = $pageData['robots'] ?? 'index, follow';
|
||||
|
||||
$currentPage = $page === 'index' ? 'Home' : ucwords(str_replace('-', ' ', $page));
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Defaults -->
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>WEBSITE | <?php echo htmlspecialchars($pageData['title'], ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
<meta name="description" content="<?php echo htmlspecialchars($description, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<meta name="author" content="WEBSITE">
|
||||
<meta name="keywords" content="<?php echo htmlspecialchars($keywords, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<meta name="robots" content="<?php echo htmlspecialchars($robots, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<link rel="canonical" href="<?php echo htmlspecialchars($pageUrl, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
|
||||
<!-- Open Graph -->
|
||||
<meta property="og:title" content="<?php echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<meta property="og:description" content="<?php echo htmlspecialchars($description, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="<?php echo htmlspecialchars($pageUrl, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<meta property="og:image" content="<?php echo $siteUrl; ?>/IMAGE">
|
||||
|
||||
<!-- Twitter Card -->
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="<?php echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<meta name="twitter:description" content="<?php echo htmlspecialchars($description, ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<meta name="twitter:image" content="<?php echo $siteUrl; ?>/IMAGE">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" href="/rss/img/favicon/favicon.ico" type="image/x-icon">
|
||||
<link rel="apple-touch-icon" href="/rss/img/favicon/apple-touch-icon.png">
|
||||
|
||||
<!-- Stylesheets -->
|
||||
<link rel="preload" href="/rss/css/main.css" as="style">
|
||||
<link href="https://cdn.jsdelivr.net/npm/@mdi/font/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/rss/css/main.css">
|
||||
<link rel="stylesheet" href="/rss/css/theme.css" media="print" onload="this.media='all'">
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="/rss/js/main.js" defer></script>
|
||||
|
||||
<!-- Structured Data (JSON-LD) -->
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "WebPage",
|
||||
"name": "<?php echo htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); ?>",
|
||||
"url": "<?php echo htmlspecialchars($pageUrl, ENT_QUOTES, 'UTF-8'); ?>",
|
||||
"description": "<?php echo htmlspecialchars($description, ENT_QUOTES, 'UTF-8'); ?>",
|
||||
"isPartOf": {
|
||||
"@type": "WebSite",
|
||||
"name": "SITE NAME",
|
||||
"url": "<?php echo $siteUrl; ?>/"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
|
||||
</header>
|
||||
43
rss/php/pageHandler.php
Normal file
43
rss/php/pageHandler.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/autoload.php');
|
||||
require_once($_SERVER['DOCUMENT_ROOT'] . '/rss/php/conf.php');
|
||||
$page = isset($_GET['page']) && !empty($_GET['page']) ? str_replace('.php', '', $_GET['page']) : 'home';
|
||||
if($page == '404'){
|
||||
http_response_code(404);
|
||||
}
|
||||
|
||||
if(file_exists($_SERVER['DOCUMENT_ROOT'] . '/rss/json/pages/' . $page . '.json')){
|
||||
// Load page configuration
|
||||
$jsonInfo = file_get_contents($_SERVER['DOCUMENT_ROOT'] . '/rss/json/pages/' . $page .'.json');
|
||||
$pageInfo = json_decode($jsonInfo, true);
|
||||
$gU = new App\Users();
|
||||
$userAuth = $gU->isAuth();
|
||||
|
||||
if($pageInfo['restricted'] && !$userAuth){
|
||||
header('location:/login/');
|
||||
}
|
||||
|
||||
if($pageInfo['redirect_login'] && $userAuth){
|
||||
header('location:/account/');
|
||||
}
|
||||
|
||||
}else{
|
||||
http_response_code(404);
|
||||
header('location:/404');
|
||||
die('Not found');
|
||||
}
|
||||
?>
|
||||
|
||||
<?php
|
||||
function loadContents(){
|
||||
global $pageInfo;
|
||||
if(file_exists($_SERVER['DOCUMENT_ROOT'] . '/rss/php/templates/'.$pageInfo['template'].'.php')){
|
||||
include($_SERVER['DOCUMENT_ROOT'] . '/rss/php/templates/'.$pageInfo['template'].'.php');
|
||||
}else{
|
||||
http_response_code(404);
|
||||
header('location:/404');
|
||||
exit();
|
||||
}
|
||||
}
|
||||
?>
|
||||
15
rss/php/templates/404.php
Normal file
15
rss/php/templates/404.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<main>
|
||||
<div class="home-banner">
|
||||
<div class="home-banner-inner">
|
||||
<div class="container">
|
||||
<div class="d-flex justify-center align-center">
|
||||
<div class="text-col">
|
||||
<h1 class="title mb-3">Oops! <span class="text-primary">Page Not Found</span></h1>
|
||||
<p class="lead mb-3">Looks like this page got lost in the search for love. Don’t worry—let’s get you back to finding your perfect match!</p>
|
||||
<a href="/" class="mb-3 btn btn-dark cta-btn">Back to home</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
696
rss/php/templates/index.php
Normal file
696
rss/php/templates/index.php
Normal file
@@ -0,0 +1,696 @@
|
||||
<div class="container">
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Typography -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Typography</h2>
|
||||
<p class="text-sm">Type scale, weight, alignment & transforms — each example followed by its code.</p>
|
||||
|
||||
<!-- Example: Type scale -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Type scale</h3>
|
||||
<div class="border rounded p-3 bg-accent-white stack-1">
|
||||
<div class="text-xxl fw-bold lh-heading">Heading XXL</div>
|
||||
<div class="text-xl fw-semibold lh-heading">Heading XL</div>
|
||||
<div class="text-lg fw-medium lh-heading">Heading LG</div>
|
||||
<div class="text-base">Body Base</div>
|
||||
<div class="text-sm text-muted">Small muted text</div>
|
||||
<div class="text-xs text-uppercase">Extra small uppercase</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="text-xxl fw-bold lh-heading">Heading XXL</div>
|
||||
<div class="text-xl fw-semibold lh-heading">Heading XL</div>
|
||||
<div class="text-lg fw-medium lh-heading">Heading LG</div>
|
||||
<div class="text-base">Body Base</div>
|
||||
<div class="text-sm text-muted">Small muted text</div>
|
||||
<div class="text-xs text-uppercase">Extra small uppercase</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Alignment -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Alignment</h3>
|
||||
<div class="border rounded p-3 bg-accent-white stack-1">
|
||||
<div class="text-left">Left aligned</div>
|
||||
<div class="text-center">Center aligned</div>
|
||||
<div class="text-right">Right aligned</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="text-left">Left aligned</div>
|
||||
<div class="text-center">Center aligned</div>
|
||||
<div class="text-right">Right aligned</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Weights & transforms -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Weights & transforms</h3>
|
||||
<div class="border rounded p-3 bg-accent-white stack-1">
|
||||
<div class="fw-light">Light</div>
|
||||
<div class="fw-normal">Normal</div>
|
||||
<div class="fw-semibold">Semibold</div>
|
||||
<div class="fw-bold text-uppercase">Uppercase bold</div>
|
||||
<div class="text-underline">Underline</div>
|
||||
<div class="text-line-through">Line-through</div>
|
||||
<div class="text-no-decoration">No decoration</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="fw-light">Light</div>
|
||||
<div class="fw-normal">Normal</div>
|
||||
<div class="fw-semibold">Semibold</div>
|
||||
<div class="fw-bold text-uppercase">Uppercase bold</div>
|
||||
<div class="text-underline">Underline</div>
|
||||
<div class="text-line-through">Line-through</div>
|
||||
<div class="text-no-decoration">No decoration</div></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Color Utilities -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Color Utilities</h2>
|
||||
<p class="text-sm">Text, background, border & spinner colors.</p>
|
||||
|
||||
<!-- Example: Text colors -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Text</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-3 gap-2">
|
||||
<div class="text-primary">Primary</div>
|
||||
<div class="text-success">Success</div>
|
||||
<div class="text-danger">Danger</div>
|
||||
<div class="text-info">Info</div>
|
||||
<div class="text-gray">Gray</div>
|
||||
<div class="text-muted">Muted</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="text-primary">Primary</div>
|
||||
<div class="text-success">Success</div>
|
||||
<div class="text-danger">Danger</div>
|
||||
<div class="text-info">Info</div>
|
||||
<div class="text-gray">Gray</div>
|
||||
<div class="text-muted">Muted</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Background colors -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Background</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-4 gap-2">
|
||||
<div class="p-2 bg-primary text-white rounded-sm text-center">Primary</div>
|
||||
<div class="p-2 bg-success text-white rounded-sm text-center">Success</div>
|
||||
<div class="p-2 bg-warning rounded-sm text-center">Warning</div>
|
||||
<div class="p-2 bg-danger text-white rounded-sm text-center">Danger</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="p-2 bg-primary text-white rounded-sm">Primary</div>
|
||||
<div class="p-2 bg-success text-white rounded-sm">Success</div>
|
||||
<div class="p-2 bg-warning rounded-sm">Warning</div>
|
||||
<div class="p-2 bg-danger text-white rounded-sm">Danger</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Border colors -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Border</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-3 gap-2">
|
||||
<div class="p-2 border border-primary rounded-sm text-center">Primary</div>
|
||||
<div class="p-2 border border-success rounded-sm text-center">Success</div>
|
||||
<div class="p-2 border border-danger rounded-sm text-center">Danger</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="p-2 border border-primary rounded-sm">Primary</div>
|
||||
<div class="p-2 border border-success rounded-sm">Success</div>
|
||||
<div class="p-2 border border-danger rounded-sm">Danger</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Spinner colors -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Spinner</h3>
|
||||
<div class="border rounded p-3 bg-accent-white flex items-center gap-3">
|
||||
<span class="spinner spinner-primary"></span>
|
||||
<span class="spinner spinner-success"></span>
|
||||
<span class="spinner spinner-danger"></span>
|
||||
<span class="spinner spinner-info"></span>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><span class="spinner spinner-primary"></span>
|
||||
<span class="spinner spinner-success"></span>
|
||||
<span class="spinner spinner-danger"></span>
|
||||
<span class="spinner spinner-info"></span></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Spacing & Sizing -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Spacing & Sizing</h2>
|
||||
<p class="text-sm">Margin/padding utilities and width/height tokens.</p>
|
||||
|
||||
<!-- Example: Margin & Padding -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Margin & Padding</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-4 gap-2">
|
||||
<div class="bg-accent-white border rounded p-2 m-1 text-center">m-1 p-2</div>
|
||||
<div class="bg-accent-white border rounded p-3 mx-2 my-1 text-center">mx-2 my-1</div>
|
||||
<div class="bg-accent-white border rounded px-4 py-2 text-center">px-4 py-2</div>
|
||||
<div class="bg-accent-white border rounded pt-5 pb-1 text-center">pt-5 pb-1</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="m-1 p-2">...</div>
|
||||
<div class="mx-2 my-1 p-3">...</div>
|
||||
<div class="px-4 py-2">...</div>
|
||||
<div class="pt-5 pb-1">...</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Percentage widths -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Percentage widths</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-4 gap-2 items-center">
|
||||
<div class="bg-info rounded text-center text-sm p-1 w-25">w-25</div>
|
||||
<div class="bg-info rounded text-center text-sm p-1 w-50">w-50</div>
|
||||
<div class="bg-info rounded text-center text-sm p-1 w-75">w-75</div>
|
||||
<div class="bg-info rounded text-center text-sm p-1 w-100">w-100</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="w-25">...</div>
|
||||
<div class="w-50">...</div>
|
||||
<div class="w-75">...</div>
|
||||
<div class="w-100">...</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Fixed & viewport sizes -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Fixed & viewport sizes</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-4 gap-2 items-center">
|
||||
<div class="bg-warning rounded text-center text-sm p-1 wpx-64">wpx-64</div>
|
||||
<div class="bg-warning rounded text-center text-sm p-1 wrem-4">wrem-4</div>
|
||||
<div class="bg-warning rounded text-center text-sm p-1 wvw-25">wvw-25</div>
|
||||
<div class="bg-warning rounded text-center text-sm p-1 hvh-25">hvh-25</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="wpx-64">...</div>
|
||||
<div class="wrem-4">...</div>
|
||||
<div class="wvw-25">...</div>
|
||||
<div class="hvh-25">...</div></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Layout (Containers, Flex Grid, CSS Grid) -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Layout</h2>
|
||||
<p class="text-sm">Containers, flex row/cols, and CSS grid helpers.</p>
|
||||
|
||||
<!-- Example: Containers -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Containers</h3>
|
||||
<div class="border rounded p-3 bg-accent-white stack-1">
|
||||
<div class="container-narrow border rounded p-2">.container-narrow</div>
|
||||
<div class="container-wide border rounded p-2">.container-wide</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="container-narrow">...</div>
|
||||
<div class="container-wide">...</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Flex grid -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Flex grid (.row / .col)</h3>
|
||||
<div class="border rounded p-3 bg-accent-white">
|
||||
<div class="row">
|
||||
<div class="col-4 p-2"><div class="border rounded p-2 text-center">col-4</div></div>
|
||||
<div class="col-4 p-2"><div class="border rounded p-2 text-center">col-4</div></div>
|
||||
<div class="col-4 p-2"><div class="border rounded p-2 text-center">col-4</div></div>
|
||||
</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="row">
|
||||
<div class="col-4">...</div>
|
||||
<div class="col-4">...</div>
|
||||
<div class="col-4">...</div>
|
||||
</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: CSS Grid -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">CSS Grid</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-3 gap-2">
|
||||
<div class="border rounded p-2 text-center">1</div>
|
||||
<div class="border rounded p-2 text-center">2</div>
|
||||
<div class="border rounded p-2 text-center">3</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="grid grid-cols-3 gap-2">
|
||||
<div>1</div>
|
||||
<div>2</div>
|
||||
<div>3</div>
|
||||
</div></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Flexbox: Alignment & Justify -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2">
|
||||
<h2 class="text-xl fw-semibold">Flexbox: Alignment & Justify</h2>
|
||||
<p class="text-sm text-muted">Use <code>.flex-*</code>, <code>.items-*</code>, and <code>.justify-*</code> to control layout.</p>
|
||||
|
||||
<!-- Live example -->
|
||||
<div class="card p-3 stack-2">
|
||||
<div class="fw-semibold">Center both axes</div>
|
||||
<div class="border rounded p-3 bg-accent-white" style="height:120px;">
|
||||
<div class="flex items-center justify-center h-100">
|
||||
<span class="pill">.flex .items-center .justify-center</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fw-semibold">Space between horizontally, start vertically</div>
|
||||
<div class="border rounded p-3 bg-accent-white">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<span class="pill">A</span><span class="pill">B</span><span class="pill">C</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fw-semibold">Column + center</div>
|
||||
<div class="border rounded p-3 bg-accent-white">
|
||||
<div class="flex-column items-center gap-1">
|
||||
<span class="pill">1</span><span class="pill">2</span><span class="pill">3</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Code -->
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="flex items-center justify-center">...</div>
|
||||
<div class="flex items-start justify-between">...</div>
|
||||
<div class="flex-column items-center">...</div></code></pre>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Grid: Item Placement (Span & Order) -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2">
|
||||
<h2 class="text-xl fw-semibold">Grid: Item Placement</h2>
|
||||
<p class="text-sm text-muted">Span columns/rows and reorder visually when needed.</p>
|
||||
|
||||
<div class="card p-3 stack-2">
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-4 gap-2">
|
||||
<div class="card p-2">1</div>
|
||||
<div class="card p-2" style="grid-column: span 2;">2 spans 2 cols</div>
|
||||
<div class="card p-2">3</div>
|
||||
<div class="card p-2" style="grid-column: 1 / -1;">4 full row</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="grid grid-cols-4">
|
||||
<div>1</div>
|
||||
<div style="grid-column: span 2;">2</div>
|
||||
<div>3</div>
|
||||
<div style="grid-column: 1 / -1;">4</div>
|
||||
</div></code></pre>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Responsive Flex vs Grid (When to use which) -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2">
|
||||
<h2 class="text-xl fw-semibold">Flex vs Grid (Quick Patterns)</h2>
|
||||
<p class="text-sm text-muted">Use flex for 1-dimensional alignment; use grid for true 2-D layouts.</p>
|
||||
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="card p-3 stack-2">
|
||||
<div class="fw-semibold">Nav bar (Flex)</div>
|
||||
<div class="border rounded p-2 bg-accent-white flex items-center justify-between">
|
||||
<span class="pill">Logo</span>
|
||||
<div class="flex gap-1">
|
||||
<span class="pill">Link</span><span class="pill">Link</span><span class="pill">Link</span>
|
||||
</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="flex items-center justify-between">...</div></code></pre>
|
||||
</div>
|
||||
|
||||
<div class="card p-3 stack-2">
|
||||
<div class="fw-semibold">Card gallery (Grid)</div>
|
||||
<div class="border rounded p-2 bg-accent-white grid grid-auto-fit-md gap-2">
|
||||
<article class="card p-2">Card</article>
|
||||
<article class="card p-2">Card</article>
|
||||
<article class="card p-2">Card</article>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="grid grid-auto-fit-md gap-2">...</div></code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Borders, Radius, Shadows -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Borders, Radius, Shadows</h2>
|
||||
<p class="text-sm">Border widths, corner radii, and shadow levels.</p>
|
||||
|
||||
<!-- Example: Borders & Radius -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Borders & radius</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-3 gap-2">
|
||||
<div class="border rounded-sm p-2 text-center">.border .rounded-sm</div>
|
||||
<div class="border-2 rounded-lg p-2 text-center">.border-2 .rounded-lg</div>
|
||||
<div class="border-3 rounded-xl p-2 text-center">.border-3 .rounded-xl</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="border rounded-sm">...</div>
|
||||
<div class="border-2 rounded-lg">...</div>
|
||||
<div class="border-3 rounded-xl">...</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Card shadows -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Card shadows</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-3 gap-2">
|
||||
<article class="card p-2">.card</article>
|
||||
<article class="card card-shadow-4 p-2">.card .card-shadow-4</article>
|
||||
<article class="card shadow-none p-2">.card .shadow-none</article>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><article class="card">...</article>
|
||||
<article class="card card-shadow-4">...</article>
|
||||
<article class="card shadow-none">...</article></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Controls (Inputs & Buttons) -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Controls</h2>
|
||||
<p class="text-sm">Shared blueprint, sizes, and variants.</p>
|
||||
|
||||
<!-- Example: Inputs -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Inputs</h3>
|
||||
<div class="border rounded p-3 bg-accent-white stack-1">
|
||||
<input class="input input-sm" placeholder="Small" />
|
||||
<input class="input input-md" placeholder="Medium" />
|
||||
<input class="input input-lg" placeholder="Large" />
|
||||
<input class="input input-xl" placeholder="XL" />
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><input class="input input-sm" placeholder="Small" />
|
||||
<input class="input input-md" placeholder="Medium" />
|
||||
<input class="input input-lg" placeholder="Large" />
|
||||
<input class="input input-xl" placeholder="XL" /></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Buttons -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Buttons</h3>
|
||||
<div class="border rounded p-3 bg-accent-white stack-1">
|
||||
<button class="btn btn-sm btn-primary">Primary</button>
|
||||
<button class="btn btn-md btn-success">Success</button>
|
||||
<button class="btn btn-lg btn-outline-danger">Outline Danger</button>
|
||||
<button class="btn btn-xl btn-ghost-info">Ghost Info</button>
|
||||
<button class="btn btn-md" disabled>Disabled</button>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><button class="btn btn-sm btn-primary">Primary</button>
|
||||
<button class="btn btn-md btn-success">Success</button>
|
||||
<button class="btn btn-lg btn-outline-danger">Outline Danger</button>
|
||||
<button class="btn btn-xl btn-ghost-info">Ghost Info</button>
|
||||
<button class="btn btn-md" disabled>Disabled</button></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Alerts -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Alerts</h2>
|
||||
<p class="text-sm">Contextual feedback banners.</p>
|
||||
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Variants</h3>
|
||||
<div class="border rounded p-3 bg-accent-white stack-1">
|
||||
<div class="alert">Base alert</div>
|
||||
<div class="alert alert-primary">Primary alert</div>
|
||||
<div class="alert alert-success">Success alert</div>
|
||||
<div class="alert alert-warning">Warning alert</div>
|
||||
<div class="alert alert-danger">Danger alert</div>
|
||||
<div class="alert alert-info">Info alert</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="alert">Base alert</div>
|
||||
<div class="alert alert-primary">Primary alert</div>
|
||||
<div class="alert alert-success">Success alert</div>
|
||||
<div class="alert alert-warning">Warning alert</div>
|
||||
<div class="alert alert-danger">Danger alert</div>
|
||||
<div class="alert alert-info">Info alert</div></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Cards -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Cards</h2>
|
||||
<p class="text-sm">Surface container with border/shadow variants.</p>
|
||||
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Examples</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-3 gap-2">
|
||||
<article class="card p-2">Default card</article>
|
||||
<article class="card card-border-primary p-2">Bordered (primary)</article>
|
||||
<article class="card card-shadow-4 p-2">Shadow level 4</article>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><article class="card">...</article>
|
||||
<article class="card card-border-primary">...</article>
|
||||
<article class="card card-shadow-4">...</article></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Tabs & Tooltips -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Tabs (With Panels)</h2>
|
||||
<p class="text-sm text-muted">
|
||||
Each tab points to a content panel by <code>href="#panel-id"</code>.
|
||||
The JS shows the matching panel and hides the others.
|
||||
</p>
|
||||
|
||||
<!-- Live example -->
|
||||
<div class="card p-3 stack-2">
|
||||
<div class="tabs">
|
||||
<a class="tab active" href="#tab-panel-1">One</a>
|
||||
<a class="tab" href="#tab-panel-2">Two</a>
|
||||
<a class="tab" href="#tab-panel-3">Three</a>
|
||||
</div>
|
||||
|
||||
<div class="stack-2 mt-2">
|
||||
<div class="tab-panel card p-2" id="tab-panel-1">Content for tab one.</div>
|
||||
<div class="tab-panel card p-2" id="tab-panel-2" hidden>Content for tab two.</div>
|
||||
<div class="tab-panel card p-2" id="tab-panel-3" hidden>Content for tab three.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Copy-paste code -->
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="tabs">
|
||||
<a class="tab active" href="#tab-panel-1">One</a>
|
||||
<a class="tab" href="#tab-panel-2">Two</a>
|
||||
<a class="tab" href="#tab-panel-3">Three</a>
|
||||
</div>
|
||||
|
||||
<div class="stack-2 mt-2">
|
||||
<div class="tab-panel card p-2" id="tab-panel-1">Content for tab one.</div>
|
||||
<div class="tab-panel card p-2" id="tab-panel-2" hidden>Content for tab two.</div>
|
||||
<div class="tab-panel card p-2" id="tab-panel-3" hidden>Content for tab three.</div>
|
||||
</div></code></pre>
|
||||
|
||||
<ul class="text-sm text-muted stack-1">
|
||||
<li>Make sure each tab’s <code>href</code> matches a panel’s <code>id</code>.</li>
|
||||
<li>Only one panel should be visible — the others have <code>hidden</code>.</li>
|
||||
<li>Keep classes exactly as shown: <code>.tabs</code>, <code>.tab</code>, <code>.tab-panel</code>.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Modal -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Modal</h2>
|
||||
<p class="text-sm text-muted">
|
||||
Click the button to open a modal. Close it by clicking the overlay or the ✕ button.
|
||||
Works with the class-based JS you added (looks for <code>.modal-open</code>, <code>.modal__overlay</code>, <code>.modal__close</code>).
|
||||
</p>
|
||||
|
||||
<!-- Live example -->
|
||||
<div class="card p-3 stack-2">
|
||||
<button class="btn btn-md btn-primary modal-open" href="#demoModal">Open demo modal</button>
|
||||
|
||||
<div class="modal" id="demoModal" aria-hidden="true" role="dialog" aria-modal="true">
|
||||
<div class="modal__overlay"></div>
|
||||
<div class="modal__dialog stack-2" role="document">
|
||||
<div class="modal__header">
|
||||
<h3 class="modal__title">Demo modal</h3>
|
||||
<button class="modal__close" aria-label="Close">✕</button>
|
||||
</div>
|
||||
<div class="stack-2">
|
||||
<p>Put your content here.</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="input input-md" placeholder="Your name" />
|
||||
<button class="btn btn-md btn-success">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Copy-paste code -->
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><button class="btn btn-md btn-primary modal-open" href="#demoModal">Open demo modal</button>
|
||||
|
||||
<div class="modal" id="demoModal" aria-hidden="true" role="dialog" aria-modal="true">
|
||||
<div class="modal__overlay"></div>
|
||||
<div class="modal__dialog" role="document">
|
||||
<div class="modal__header">
|
||||
<h3 class="modal__title">Demo modal</h3>
|
||||
<button class="modal__close" aria-label="Close">✕</button>
|
||||
</div>
|
||||
<div>
|
||||
<p>Put your content here.</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<input class="input input-md" placeholder="Your name" />
|
||||
<button class="btn btn-md btn-success">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></code></pre>
|
||||
|
||||
<!-- Tips -->
|
||||
<ul class="text-sm text-muted stack-1">
|
||||
<li>Trigger: <code>.modal-open</code> on a button/link. Use <code>href="#modalId"</code> or <code>data-target="#modalId"</code>.</li>
|
||||
<li>Close: add <code>.modal__close</code> to a button or click the <code>.modal__overlay</code>.</li>
|
||||
<li>Only change the <code>id</code> and the content — keep the classes the same.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Utilities -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">Utilities</h2>
|
||||
<p class="text-sm">Motion, overflow, ratio, cursors & opacity.</p>
|
||||
|
||||
<!-- Example: Motion & text -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Motion & text</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-3 gap-2">
|
||||
<div class="p-2 border rounded hover-raise text-center">.hover-raise</div>
|
||||
<div class="p-2 border rounded hover-glow text-center">.hover-glow</div>
|
||||
<div class="p-2 border rounded truncate w-50">This is a very long line that will be truncated.</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="hover-raise">...</div>
|
||||
<div class="hover-glow">...</div>
|
||||
<div class="truncate w-50">Long long text...</div></code></pre>
|
||||
</div>
|
||||
|
||||
<!-- Example: Effects, overflow, ratios -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">Effects, overflow, ratios</h3>
|
||||
<div class="border rounded p-3 bg-accent-white grid grid-cols-3 gap-2">
|
||||
<div class="p-2 border rounded opacity-50 text-center">.opacity-50</div>
|
||||
<div class="p-2 border rounded cursor-not-allowed text-center">.cursor-not-allowed</div>
|
||||
<div class="p-2 border rounded overflow-scroll" style="max-height:3rem">
|
||||
.overflow-scroll<br/>Line 2<br/>Line 3<br/>Line 4
|
||||
</div>
|
||||
<div class="p-2 border rounded ratio ratio-16x9"><img class="object-cover" src="https://picsum.photos/640/360" alt=""></div>
|
||||
<div class="p-2 border rounded ratio ratio-1x1"><img class="object-contain" src="https://picsum.photos/512" alt=""></div>
|
||||
<div class="p-2 border rounded blur-sm text-center">.blur-sm</div>
|
||||
</div>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code><div class="opacity-50">...</div>
|
||||
<div class="cursor-not-allowed">...</div>
|
||||
<div class="overflow-scroll">...</div>
|
||||
<div class="ratio ratio-16x9"><img class="object-cover" ... /></div>
|
||||
<div class="ratio ratio-1x1"><img class="object-contain" ... /></div>
|
||||
<div class="blur-sm">...</div></code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- JavaScript Helpers (Reference) -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h2 class="text-xl fw-semibold">JavaScript Helpers (Reference)</h2>
|
||||
<p class="text-sm">
|
||||
Lightweight utilities used across the docs and components. Keep logic simple and colocated.
|
||||
This card describes the functions and expected usage; copy snippets as needed.
|
||||
</p>
|
||||
|
||||
<!-- HTTP -->
|
||||
<div class="card p-3 stack-2">
|
||||
<h3 class="text-lg fw-medium">HTTP</h3>
|
||||
<p class="text-sm">Post JSON-ish data via <code>FormData</code> to the PHP handler.</p>
|
||||
|
||||
<div class="stack-1">
|
||||
<div>
|
||||
<div class="fw-semibold">_post(formObj, target, func)</div>
|
||||
<p class="text-sm text-gray">Builds a <code>FormData</code> payload and POSTs to <code>/rss/php/handler.php</code>, returning JSON.</p>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code>// Example
|
||||
const res = await _post(
|
||||
{ name: 'Ada', roles: ['admin','editor'] },
|
||||
'users',
|
||||
'create'
|
||||
);
|
||||
// res → parsed JSON (or { ok:false, error:"..." } on failure)</code></pre>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="fw-semibold">createFormData(data, target, func)</div>
|
||||
<p class="text-sm text-gray">Converts plain objects/arrays into <code>FormData</code> and appends <code>target</code>/<code>function</code>.</p>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code>// Example
|
||||
const fd = await createFormData({ tags: ['a','b'], avatar: file }, 'profile', 'update');</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- DOM -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h3 class="text-lg fw-medium">DOM</h3>
|
||||
<p class="text-sm text-muted">Small helpers to create elements and IDs.</p>
|
||||
|
||||
<div class="stack-2">
|
||||
<div class="card p-3 stack-1">
|
||||
<div class="fw-semibold">createEl(tag, classArr?, text?, dataAttrs?)</div>
|
||||
<p class="text-sm text-gray">Create an element, add classes, text, and <code>data-*</code> attributes.</p>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code>// Example
|
||||
const btn = createEl('button', ['btn','btn-md','btn-primary'], 'Save', { action: 'save' });
|
||||
document.body.append(btn);</code></pre>
|
||||
</div>
|
||||
|
||||
<div class="card p-3 stack-1">
|
||||
<div class="fw-semibold">getRandomID()</div>
|
||||
<p class="text-sm text-gray">Generates a unique id (uses <code>crypto.randomUUID()</code> if available).</p>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code>// Example
|
||||
const id = getRandomID(); // "c1c0f33a-..." or base36 fallback</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- ====================================================================== -->
|
||||
<!-- Alerts -->
|
||||
<!-- ====================================================================== -->
|
||||
<section class="card p-3 stack-2 my-2">
|
||||
<h3 class="text-lg fw-medium">Alerts</h3>
|
||||
<p class="text-sm text-muted">Create and remove framework-styled alerts using <code>.alert</code> and <code>.alert-{color}</code>.</p>
|
||||
|
||||
<div class="card p-3 stack-2">
|
||||
<div class="fw-semibold">generateAlert(type, text, options?) → HTMLElement</div>
|
||||
<p class="text-sm text-gray">
|
||||
Types align with your color tokens: <code>primary</code>, <code>success</code>, <code>warning</code>, <code>danger</code>, <code>info</code>, etc.
|
||||
</p>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code>// Create and insert
|
||||
const alertEl = await generateAlert('warning', 'Heads up!', { withClose: true, dismissAfter: 4000 });
|
||||
document.querySelector('#alertHost').append(alertEl);
|
||||
|
||||
// Common types
|
||||
await generateAlert('success', 'Saved!');
|
||||
await generateAlert('danger', 'Something went wrong', { role: 'alert' });</code></pre>
|
||||
|
||||
<div class="fw-semibold">removeAlert(scope?)</div>
|
||||
<p class="text-sm text-gray">Remove all alerts in <code>scope</code> (default: <code>document</code>).</p>
|
||||
<pre class="border rounded p-2 bg-accent-white text-sm"><code>// Example
|
||||
removeAlert(); // or removeAlert(document.querySelector('#alertHost'));</code></pre>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
8
sitemap.xml
Normal file
8
sitemap.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://DOMAIN.com/</loc>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
</urlset>
|
||||
Reference in New Issue
Block a user