├── .gitignore
├── .npmrc
├── .stackblitzrc
├── .vscode
└── launch.json
├── LICENSE
├── README.md
├── astro.config.mjs
├── jsconfig.json
├── package.json
├── postcss.config.cjs
├── public
├── favicon.ico
├── fonts
│ ├── PlusJakartaSans-Bold.woff
│ ├── PlusJakartaSans-Bold.woff2
│ ├── PlusJakartaSans-BoldItalic.woff
│ ├── PlusJakartaSans-BoldItalic.woff2
│ ├── PlusJakartaSans-ExtraBold.woff
│ ├── PlusJakartaSans-ExtraBold.woff2
│ ├── PlusJakartaSans-ExtraBoldItalic.woff
│ ├── PlusJakartaSans-ExtraBoldItalic.woff2
│ ├── PlusJakartaSans-ExtraLight.woff
│ ├── PlusJakartaSans-ExtraLight.woff2
│ ├── PlusJakartaSans-ExtraLightItalic.woff
│ ├── PlusJakartaSans-ExtraLightItalic.woff2
│ ├── PlusJakartaSans-Italic.woff
│ ├── PlusJakartaSans-Italic.woff2
│ ├── PlusJakartaSans-Light.woff
│ ├── PlusJakartaSans-Light.woff2
│ ├── PlusJakartaSans-LightItalic.woff
│ ├── PlusJakartaSans-LightItalic.woff2
│ ├── PlusJakartaSans-Medium.woff
│ ├── PlusJakartaSans-Medium.woff2
│ ├── PlusJakartaSans-MediumItalic.woff
│ ├── PlusJakartaSans-MediumItalic.woff2
│ ├── PlusJakartaSans-Regular.woff
│ ├── PlusJakartaSans-Regular.woff2
│ ├── PlusJakartaSans-SemiBold.woff
│ ├── PlusJakartaSans-SemiBold.woff2
│ ├── PlusJakartaSans-SemiBoldItalic.woff
│ └── PlusJakartaSans-SemiBoldItalic.woff2
└── robots.txt
├── snowpack.config.mjs
├── src
├── components
│ ├── Alert.astro
│ ├── Avatar.astro
│ ├── Badge.astro
│ ├── BaseHead.astro
│ ├── Button.astro
│ ├── Card.astro
│ ├── Checkbox.astro
│ ├── ContentSection.astro
│ ├── ContentSectionTitle.astro
│ ├── DataCounter.astro
│ ├── Dialog.astro
│ ├── IconBox.astro
│ ├── IconButton.astro
│ ├── Input.astro
│ ├── Label.astro
│ ├── Link.astro
│ ├── Logo.astro
│ ├── Menu.astro
│ ├── Navbar.astro
│ ├── NewMenu.astro
│ ├── Pagination.astro
│ ├── Select.astro
│ ├── Sidebar.astro
│ ├── SidebarHeading.astro
│ ├── SidebarItem.astro
│ ├── SmallPagination.astro
│ ├── StatsCard.astro
│ ├── TBody.astro
│ ├── THead.astro
│ ├── Td.astro
│ ├── Textbox.astro
│ └── Th.astro
├── dummies
│ ├── orders.js
│ ├── products.js
│ └── users.js
├── layouts
│ ├── AppLayout.astro
│ └── AuthLayout.astro
├── pages
│ ├── 404.astro
│ ├── _home
│ │ ├── Overview.astro
│ │ ├── PopularProduct.astro
│ │ └── RecentOrder.astro
│ ├── auth
│ │ ├── email-verification.astro
│ │ ├── forgot-password.astro
│ │ ├── login.astro
│ │ └── register.astro
│ ├── blank.astro
│ ├── index.astro
│ ├── profile
│ │ ├── _components
│ │ │ ├── BrowserSession.astro
│ │ │ ├── ProfileInformation.astro
│ │ │ └── UpdatePassword.astro
│ │ └── index.astro
│ └── users
│ │ ├── _components
│ │ ├── CreateUserDialog.astro
│ │ ├── Filter.astro
│ │ ├── Order.astro
│ │ └── Table.astro
│ │ └── index.astro
├── scripts
│ ├── dialog.js
│ ├── main.js
│ ├── menu.js
│ ├── popover.js
│ ├── sidebar.js
│ └── sidebarsub.js
└── styles
│ └── global.css
├── tailwind.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # build output
2 | dist
3 |
4 | # dependencies
5 | node_modules/
6 | .snowpack/
7 |
8 | # logs
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 |
13 | # environment variables
14 | .env
15 | .env.production
16 |
17 | # macOS-specific files
18 | .DS_Store
19 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | ## force pnpm to hoist
2 | shamefully-hoist = true
3 |
--------------------------------------------------------------------------------
/.stackblitzrc:
--------------------------------------------------------------------------------
1 | {
2 | "startCommand": "npm start",
3 | "env": {
4 | "ENABLE_CJS_IMPORTS": true
5 | }
6 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "args": [
9 | "--extensionDevelopmentPath=${workspaceFolder}"
10 | ],
11 | "name": "Launch Extension",
12 | "outFiles": [
13 | "${workspaceFolder}/out/**/*.js"
14 | ],
15 | "preLaunchTask": "npm",
16 | "request": "launch",
17 | "type": "pwa-extensionHost"
18 | },
19 | ]
20 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 ruine.dev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ruine UI Dashboard
2 |
3 | 
4 | A responsive and accessible admin dashboard template built using ruine UI, a TailwindCSS based
5 | design system, and AlpineJS.
6 |
7 | See the demo on https://ruine-dashboard.pages.dev/
8 |
9 | ## Installation
10 |
11 | There're two ways on how to get this template:
12 |
13 | ### Clone this repository
14 |
15 | ```bash
16 | git clone https://github.com/ruine-dev/ruine-ui-dashboard.git
17 | ```
18 |
19 | This template is created by using [Astro](https://astro.build), check their
20 | [documentation](https://docs.astro.build/getting-started/) on how to use it.
21 |
22 | ### Download the released compiled version
23 |
24 | You could also download it from this repository
25 | [releases page](https://github.com/ruine-dev/ruine-ui-dashboard/releases).
26 |
27 | Move all the assets into your project, then replace the Tailwind config and CSS file with the one
28 | this template provides.
29 |
30 | ## Feature
31 |
32 | ### Pages
33 |
34 | These are the available pages from this template that you could use for your project:
35 |
36 | - Dashboard page
37 | - Profile page
38 | - User list page
39 | - User details page
40 | - Login page
41 | - Register page
42 | - Email verification page
43 | - Forgot password page
44 | - 404 page
45 |
46 | ### Components
47 |
48 | In those mentioned pages already included several components, such as:
49 |
50 | - Alert
51 | - Avatar
52 | - Badge
53 | - Button
54 | - Card
55 | - Checkbox
56 | - Dialog / Modal
57 | - Icon Box
58 | - Icon Button
59 | - Input & Label
60 | - Menu / Dropdown
61 | - Pagination
62 | - Popover
63 | - Select
64 | - Table
65 |
66 | ## License
67 |
68 | **ruine UI Dashboard** is licensed under [MIT](https://opensource.org/licenses/MIT).
69 |
70 | ## Special Thanks
71 |
72 | - [Astro](https://astro.build)
73 | - [TailwindCSS](https://tailwindcss.com)
74 | - [Akar Icons](https://akaricons.com)
75 | - [AlpineJS](https://alpinejs.dev)
76 | - [Unsplash](https://unsplash.com)
77 | - [Random User Generator](https://randomuser.me)
78 |
--------------------------------------------------------------------------------
/astro.config.mjs:
--------------------------------------------------------------------------------
1 | // Full Astro Configuration API Documentation:
2 | // https://docs.astro.build/reference/configuration-reference
3 |
4 | // @type-check enabled!
5 | // VSCode and other TypeScript-enabled text editors will provide auto-completion,
6 | // helpful tooltips, and warnings if your exported object is invalid.
7 | // You can disable this by removing "@ts-check" and `@type` comments below.
8 |
9 | // @ts-check
10 | export default /** @type {import('astro').AstroUserConfig} */ ({
11 | // Comment out "renderers: []" to enable Astro's default component support.
12 | renderers: [],
13 | devOptions: {
14 | tailwindConfig: './tailwind.config.js',
15 | },
16 | });
17 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "$components/*": ["src/components/*"],
6 | "$layouts/*": ["src/layouts/*"],
7 | "$dummies/*": ["src/dummies/*"],
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@example/minimal",
3 | "version": "0.0.1",
4 | "private": true,
5 | "scripts": {
6 | "dev": "astro dev",
7 | "start": "astro dev",
8 | "build": "astro build",
9 | "preview": "astro preview"
10 | },
11 | "devDependencies": {
12 | "alpinejs": "^3.7.1",
13 | "astro": "0.20.12",
14 | "autoprefixer": "^10.4.0",
15 | "clsx": "^1.1.1",
16 | "postcss": "^8.4.5",
17 | "tailwindcss": "^3.0.7"
18 | },
19 | "dependencies": {
20 | "@alpinejs/collapse": "^3.7.1",
21 | "@alpinejs/trap": "^3.7.1"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/postcss.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {},
4 | tailwindcss: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/favicon.ico
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Bold.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Bold.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-BoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-BoldItalic.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-BoldItalic.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-ExtraBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-ExtraBold.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-ExtraBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-ExtraBold.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-ExtraBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-ExtraBoldItalic.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-ExtraBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-ExtraBoldItalic.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-ExtraLight.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-ExtraLight.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-ExtraLight.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-ExtraLight.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-ExtraLightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-ExtraLightItalic.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-ExtraLightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-ExtraLightItalic.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Italic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Italic.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Italic.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Light.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Light.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-LightItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-LightItalic.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-LightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-LightItalic.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Medium.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Medium.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Medium.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Medium.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-MediumItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-MediumItalic.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-MediumItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-MediumItalic.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Regular.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-Regular.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-SemiBold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-SemiBold.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-SemiBold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-SemiBold.woff2
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-SemiBoldItalic.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-SemiBoldItalic.woff
--------------------------------------------------------------------------------
/public/fonts/PlusJakartaSans-SemiBoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ruine-dev/ruine-ui-dashboard/94b91fbfb0e3c591bb31ddd02478db15b3ac98f8/public/fonts/PlusJakartaSans-SemiBoldItalic.woff2
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/snowpack.config.mjs:
--------------------------------------------------------------------------------
1 | export default {
2 | alias: {
3 | $components: './src/components',
4 | $layouts: './src/layouts',
5 | $dummies: './src/dummies',
6 | },
7 | };
8 |
--------------------------------------------------------------------------------
/src/components/Alert.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { title, className, color, ...props } = Astro.props;
5 |
6 | const colors = {
7 | success: 'bg-success-100 text-success-800 border border-success-300',
8 | danger: 'bg-danger-100 text-danger-800 border border-danger-300',
9 | warning: 'bg-warning-100 text-warning-800 border border-warning-300',
10 | info: 'bg-info-100 text-info-800 border border-info-300',
11 | };
12 | ---
13 |
14 |
{Boolean(title) && {title} }
15 |
--------------------------------------------------------------------------------
/src/components/Avatar.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | const { src, alt, size = 'md', shape = 'circle', className } = Astro.props;
4 |
5 | const shapes = {
6 | circle: 'rounded-full',
7 | square: 'rounded-lg',
8 | };
9 |
10 | const sizes = {
11 | 'xs': 'w-6 h-6',
12 | 'sm': 'w-8 h-8',
13 | 'md': 'w-10 h-10',
14 | 'lg': 'w-12 h-12',
15 | 'xl': 'w-14 h-14',
16 | };
17 |
18 | const sizeValues = {
19 | 'xs': '1.5rem',
20 | 'sm': '2rem',
21 | 'md': '2.5rem',
22 | 'lg': '3rem',
23 | 'xl': '3.5rem',
24 | }
25 | ---
26 |
27 |
--------------------------------------------------------------------------------
/src/components/Badge.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | const { color, className } = Astro.props;
4 |
5 | const colors = {
6 | gray: 'bg-gray-100 text-gray-800',
7 | red: 'bg-red-100 text-red-800',
8 | green: 'bg-green-100 text-green-800',
9 | blue: 'bg-blue-100 text-blue-800',
10 | yellow: 'bg-yellow-100 text-yellow-800',
11 | purple: 'bg-purple-100 text-purple-800',
12 | pink: 'bg-pink-100 text-pink-800',
13 | indigo: 'bg-indigo-100 text-indigo-800',
14 | }
15 | ---
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/components/BaseHead.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const { title } = Astro.props;
3 | ---
4 |
5 |
6 |
7 | {Boolean(title) ? {title} - ruine UI : ruine UI Dashboard }
8 |
--------------------------------------------------------------------------------
/src/components/Button.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { as = 'button', size = 'md', color = 'white', className, ...props } = Astro.props;
5 |
6 | const getSize = (size) => {
7 | const sizes = {
8 | sm: 'py-1 px-4',
9 | md: 'py-3 sm:py-2 px-6',
10 | lg: 'py-3 px-8 text-lg',
11 | };
12 |
13 | return sizes[size];
14 | };
15 |
16 | const getColor = (color) => {
17 | const noBorder = 'border border-transparent';
18 | const noTransparent = 'ring-offset-2 shadow-sm';
19 | const noWhiteClasses = clsx('text-white', noBorder);
20 | const noDangerClasses = 'ring-primary-600';
21 |
22 | const colors = {
23 | primary: clsx(
24 | 'bg-primary-600 hover:bg-primary-700 ',
25 | noWhiteClasses,
26 | noDangerClasses,
27 | noTransparent,
28 | ),
29 | danger: clsx('bg-danger-600 hover:bg-danger-700 ', noWhiteClasses, noTransparent),
30 | white: clsx(
31 | 'bg-white hover:bg-gray-50 text-gray-700 border border-gray-300',
32 | noDangerClasses,
33 | noTransparent,
34 | ),
35 | transparent: clsx('hover:bg-gray-100 text-gray-700', noBorder),
36 | };
37 |
38 | return colors[color];
39 | };
40 |
41 | const classes = clsx(
42 | 'disabled:opacity-60 inline-flex rounded-lg font-medium active:scale-90 transition outline-none focus:ring-2 tracking-wide disabled:cursor-not-allowed',
43 | getSize(size),
44 | getColor(color),
45 | className,
46 | );
47 | ---
48 | {as === 'a' ? (
49 |
50 |
51 |
52 | ) : (
53 |
54 |
55 |
56 | )}
--------------------------------------------------------------------------------
/src/components/Card.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { className, rounded } = Astro.props;
5 | ---
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/src/components/Checkbox.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | import Label from './Label.astro';
4 |
5 | const { label, name, id, className, ...props } = Astro.props;
6 | ---
7 |
8 |
9 | {label}
10 |
--------------------------------------------------------------------------------
/src/components/ContentSection.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | const { className, ...props } = Astro.props;
4 | ---
5 |
6 |
--------------------------------------------------------------------------------
/src/components/ContentSectionTitle.astro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/DataCounter.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { active = 1, showing = 10, count = 10, className, ...props } = Astro.props;
5 | ---
6 | Showing {(showing * (active - 1)) + 1} to {(showing * (active - 1)) + showing} of {count} results
--------------------------------------------------------------------------------
/src/components/Dialog.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | import IconButton from './IconButton.astro';
4 |
5 | const { title, open, onClose, className, ...props } = Astro.props;
6 | ---
7 | document.addEventListener('keydown', (event) => {
11 | if (event.key === 'Escape' && ${open}) {
12 | ${onClose}
13 | }
14 | }))`}
15 | class="fixed top-0 left-0 z-20 w-full h-full overflow-y-auto bg-gray-800/80"
16 | x-transition:enter="transition ease-out duration-300"
17 | x-transition:enter-start="opacity-0"
18 | x-transition:enter-end="opacity-100"
19 | x-transition:leave="transition ease-in duration-300"
20 | x-transition:leave-start="opacity-100"
21 | x-transition:leave-end="opacity-0"
22 | >
23 |
26 |
40 |
41 |
42 |
43 | {title}
44 |
45 |
46 |
47 | Close dialog
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/src/components/IconBox.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { size = 'md' } = Astro.props;
5 |
6 | const getSize = (size) => {
7 | const sizes = {
8 | sm: 'w-8 h-8',
9 | md: 'w-12 h-12',
10 | lg: 'w-16 h-16',
11 | }
12 |
13 | return sizes[size];
14 | }
15 | ---
16 |
17 |
21 |
22 |
--------------------------------------------------------------------------------
/src/components/IconButton.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { as = 'button', color = 'transparent', size = 'md', noHover, className, ...props } = Astro.props;
5 |
6 | const getSize = (size) => {
7 | const sizes = {
8 | sm: 'p-2 sm:p-1',
9 | md: 'p-3 sm:p-2',
10 | lg: 'p-3',
11 | };
12 |
13 | return sizes[size];
14 | };
15 |
16 | const getColor = (color) => {
17 | const noBorder = 'border border-transparent';
18 | const noTransparent = 'ring-offset-2 shadow-sm';
19 | const noWhiteClasses = clsx('text-white', noBorder);
20 | const noDangerClasses = 'ring-primary-600';
21 |
22 | const colors = {
23 | primary: clsx(
24 | 'bg-primary-600 hover:bg-primary-700 ',
25 | noWhiteClasses,
26 | noDangerClasses,
27 | noTransparent,
28 | ),
29 | danger: clsx('bg-danger-600 hover:bg-danger-700 ', noWhiteClasses, noTransparent),
30 | white: clsx(
31 | 'bg-white hover:bg-gray-50 text-gray-700 border border-gray-300',
32 | noDangerClasses,
33 | noTransparent,
34 | ),
35 | transparent: clsx('text-gray-700', noBorder, noDangerClasses, {'hover:bg-gray-100': !noHover}),
36 | };
37 |
38 | return colors[color];
39 | }
40 |
41 | const classes = clsx(
42 | 'inline-flex transition rounded-lg outline-none disabled:cursor-not-allowed disabled:opacity-60 active:scale-90 focus:ring-2',
43 | getSize(size),
44 | getColor(color),
45 | className
46 | );
47 | ---
48 | {as === 'a' ? (
49 |
50 |
51 |
52 | ) : (
53 |
54 |
55 |
56 | )}
57 |
--------------------------------------------------------------------------------
/src/components/Input.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { name, type, placeholder, id, className, leading, trailing, ...props } = Astro.props;
5 | const getPadding = () => {
6 | return clsx('py-3 sm:py-2', {
7 | 'pl-11 sm:pl-10 pr-3': leading,
8 | 'pl-3 pr-11 sm:pr-10': trailing,
9 | 'px-3': !leading && !trailing
10 | });
11 | }
12 | ---
13 |
14 |
18 | {Boolean(leading) && (
19 |
20 | {leading}
21 |
22 | )}
23 |
28 | {Boolean(trailing) && (
29 |
30 | {trailing}
31 |
32 | )}
33 |
--------------------------------------------------------------------------------
/src/components/Label.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const { htmlFor } = Astro.props;
3 | ---
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/components/Link.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { as = 'a', color = 'primary', className, ...props } = Astro.props;
5 |
6 | const noDangerClasses = 'ring-primary-600';
7 |
8 | const colors = {
9 | primary: clsx('text-primary-600 hover:text-primary-700', noDangerClasses),
10 | danger: clsx('text-danger-600 hover:text-danger-700 ring-danger-600'),
11 | passive: clsx('text-gray-600 hover:text-gray-700', noDangerClasses),
12 | };
13 |
14 | const classes = clsx('font-medium outline-none focus:ring-2 rounded', colors[color], className);
15 | ---
16 | {as === 'button' ? (
17 |
18 |
19 |
20 | ) : (
21 |
22 |
23 |
24 | )}
25 |
--------------------------------------------------------------------------------
/src/components/Logo.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { href, className } = Astro.props;
5 | const baseClass = clsx('font-medium tracking-tight text-gray-700', className);
6 | const subClass = clsx('font-semibold text-primary-600');
7 | ---
8 | {Boolean(href) ? (
9 |
10 | ruine.UI
11 |
12 | ) : (
13 | ruine.UI
14 | )}
15 |
--------------------------------------------------------------------------------
/src/components/Menu.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const { items } = Astro.props;
3 | const stringItems = JSON.stringify(items);
4 | const menuItems = stringItems.replace(/\"/g, "'");
5 | ---
6 |
--------------------------------------------------------------------------------
/src/components/Navbar.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | import Avatar from './Avatar.astro';
4 | import Menu from './Menu.astro';
5 | import IconButton from './IconButton.astro';
6 | import Input from './Input.astro';
7 |
8 | const notifications = [
9 | {
10 | icon: ,
11 | title: 'New user registered',
12 | type: 'success',
13 | time: '2 min ago',
14 | },
15 | {
16 | icon: ,
17 | title: 'New order received',
18 | type: 'info',
19 | time: '5 min ago',
20 | },
21 | {
22 | icon: ,
23 | title: 'Order #R-2384 need to be processed',
24 | type: 'warning',
25 | time: '14 min ago',
26 | },
27 | {
28 | icon: ,
29 | title: 'Order #R-2157 just got a bad review',
30 | type: 'danger',
31 | time: '18 min ago',
32 | },
33 | {
34 | icon: ,
35 | title: 'Order #R-2206 is completed',
36 | type: 'success',
37 | time: '25 min ago',
38 | },
39 | ];
40 |
41 | const notificationColor = {
42 | 'success': 'bg-success-100 text-success-800',
43 | 'warning': 'bg-warning-100 text-warning-800',
44 | 'danger': 'bg-danger-100 text-danger-800',
45 | 'info': 'bg-info-100 text-info-800',
46 | }
47 |
48 | const userMenu = [
49 | [
50 | {
51 | icon: ' ',
52 | label: 'Profile',
53 | href: '/profile',
54 | },
55 | {
56 | icon: ' ',
57 | label: 'Settings',
58 | href: '#',
59 | }
60 | ],
61 | [
62 | {
63 | icon: ' ',
64 | label: 'Log out',
65 | href: '/auth/login',
66 | }
67 | ]
68 | ];
69 | ---
70 |
71 |
72 |
73 |
74 |
75 |
76 | Toggle sidebar
77 |
78 |
}/>
79 |
80 |
81 |
82 |
83 |
84 | Toggle notifications
85 |
86 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/src/components/NewMenu.astro:
--------------------------------------------------------------------------------
1 | ---
2 | const { itemGroups } = Astro.props;
3 | ---
4 |
5 |
6 |
12 |
13 | {itemGroups.map(group => (
14 |
35 | ))}
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/components/Pagination.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | import IconButton from './IconButton.astro';
4 |
5 | const { active = 1, showing = 10, count = 10, className } = Astro.props;
6 |
7 | const range = Array(count / showing).fill(0).map((_, i) => i + 1);
8 | ---
9 |
10 |
11 |
12 |
13 | {range.map(page => (
14 |
18 |
19 | {page}
20 |
21 |
22 | ))}
23 |
...
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/components/Select.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { name, id, placeholder, className, ...props } = Astro.props;
5 | ---
6 |
12 | {Boolean(placeholder) && {placeholder} }
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/Sidebar.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import IconButton from './IconButton.astro';
3 | import Logo from './Logo.astro';
4 | import SidebarHeading from './SidebarHeading.astro';
5 | import SidebarItem from './SidebarItem.astro';
6 |
7 | const menu = [
8 | {
9 | icon: ,
10 | label: 'Dashboard',
11 | href: '/'
12 | },
13 | {
14 | icon: ,
15 | label: 'Auth',
16 | items: [
17 | {
18 | label: 'Login',
19 | href: '/auth/login'
20 | },
21 | {
22 | label: 'Register',
23 | href: '/auth/register'
24 | },
25 | {
26 | label: 'Forgot Password',
27 | href: '/auth/forgot-password'
28 | },
29 | {
30 | label: 'Email Verification',
31 | href: '/auth/email-verification'
32 | }
33 | ],
34 | },
35 | {
36 | icon: ,
37 | label: 'Miscellaneous',
38 | items: [
39 | {
40 | label: 'Blank',
41 | href: '/blank'
42 | },
43 | {
44 | label: '404',
45 | href: '/404'
46 | },
47 | ]
48 | },
49 | {
50 | icon: ,
51 | label: 'Users',
52 | href: '/users'
53 | },
54 | ];
55 | ---
56 |
57 |
67 |
78 |
79 |
80 | Close sidebar
81 |
82 |
83 |
84 |
85 |
86 |
87 | Main Menu
88 | {menu.map(({icon, label, href, items}) => (
89 |
90 | {icon} {label}
91 |
92 | ))}
93 |
94 |
95 |
--------------------------------------------------------------------------------
/src/components/SidebarHeading.astro:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/SidebarItem.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { href = '#', items = [] } = Astro.props;
5 | const { pathname } = Astro.request.canonicalURL;
6 | const isActive = href => href === (pathname.length > 1 ? pathname.slice(0, -1) : '/');
7 | const hasActiveItem = items.some(item => isActive(item.href));
8 | const classes = (href = null) => clsx('transition w-full flex font-medium block rounded-lg py-3 px-3 -ml-3 outline-none focus:ring-2 ring-primary-600', {
9 | 'hover:bg-primary-50 hover:text-primary-600': !isActive(href),
10 | 'bg-primary-100 text-primary-800': isActive(href),
11 | });
12 | ---
13 |
14 |
15 | {items.length > 0 ? (
16 |
17 |
18 |
19 |
20 |
21 |
40 |
41 | ) : (
42 |
46 |
47 |
48 | )}
49 |
--------------------------------------------------------------------------------
/src/components/SmallPagination.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Button from './Button.astro';
3 | ---
4 |
5 |
6 | Previous
7 | Showing 11 to 20 of 100 results
8 | Next
9 |
--------------------------------------------------------------------------------
/src/components/StatsCard.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | import Card from './Card.astro';
4 | import IconBox from './IconBox.astro';
5 |
6 | const { label, value, icon, className } = Astro.props;
7 | ---
8 |
9 |
10 |
11 | {icon}
12 |
13 |
14 |
{label}
15 |
{value}
16 |
17 |
--------------------------------------------------------------------------------
/src/components/TBody.astro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/components/THead.astro:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/components/Td.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { padding = 'p-4', className } = Astro.props;
5 | ---
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/components/Textbox.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | import Input from './Input.astro';
4 | import Label from './Label.astro';
5 |
6 | const { name, id, label, className, ...props } = Astro.props;
7 | ---
8 |
9 |
10 | {label}
11 |
12 |
--------------------------------------------------------------------------------
/src/components/Th.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 |
4 | const { padding = 'p-4', className } = Astro.props;
5 | ---
6 |
--------------------------------------------------------------------------------
/src/dummies/orders.js:
--------------------------------------------------------------------------------
1 | import { users } from './users.js';
2 |
3 | export const orders = [
4 | {
5 | id: 1,
6 | amount: '65.94',
7 | date: 'November 01, 2021',
8 | status: 'Pending',
9 | buyer: users[0],
10 | },
11 | {
12 | id: 2,
13 | amount: '51.61',
14 | date: 'October 31, 2021',
15 | status: 'Pending',
16 | buyer: users[1],
17 | },
18 | {
19 | id: 3,
20 | amount: '40.32',
21 | date: 'October 30, 2021',
22 | status: 'Processed',
23 | buyer: users[2],
24 | },
25 | {
26 | id: 4,
27 | amount: '57.89',
28 | date: 'October 30, 2021',
29 | status: 'Canceled',
30 | buyer: users[3],
31 | },
32 | {
33 | id: 5,
34 | amount: '90.29',
35 | date: 'October 29, 2021',
36 | status: 'Processed',
37 | buyer: users[4],
38 | },
39 | {
40 | id: 6,
41 | amount: '94.93',
42 | date: 'October 29, 2021',
43 | status: 'Completed',
44 | buyer: users[5],
45 | },
46 | {
47 | id: 7,
48 | amount: '66.65',
49 | date: 'October 29, 2021',
50 | status: 'Completed',
51 | buyer: users[6],
52 | },
53 | {
54 | id: 8,
55 | amount: '65.52',
56 | date: 'October 27, 2021',
57 | status: 'Canceled',
58 | buyer: users[7],
59 | },
60 | {
61 | id: 9,
62 | amount: '74.66',
63 | date: 'October 26, 2021',
64 | status: 'Completed',
65 | buyer: users[8],
66 | },
67 | {
68 | id: 10,
69 | amount: '35.38',
70 | date: 'October 26, 2021',
71 | status: 'Completed',
72 | buyer: users[9],
73 | },
74 | ];
75 |
76 | export const statuses = {
77 | Pending: 'yellow',
78 | Processed: 'blue',
79 | Completed: 'green',
80 | Canceled: 'gray',
81 | };
82 |
--------------------------------------------------------------------------------
/src/dummies/products.js:
--------------------------------------------------------------------------------
1 | export const products = [
2 | {
3 | id: 1,
4 | name: 'Black Canon Camera',
5 | price: 350,
6 | rating: 4.3,
7 | sold: 352,
8 | image: 'https://source.unsplash.com/W2Dta_Yiwfw',
9 | },
10 | {
11 | id: 2,
12 | name: 'White Wireless Earphone',
13 | price: 150,
14 | rating: 5.0,
15 | sold: 341,
16 | image: 'https://source.unsplash.com/SBLT7JohtCo',
17 | },
18 | {
19 | id: 3,
20 | name: 'Black Headset',
21 | price: 130,
22 | rating: 3.9,
23 | sold: 295,
24 | image: 'https://source.unsplash.com/dBwadhWa-lI',
25 | },
26 | {
27 | id: 4,
28 | name: 'Playstation 5',
29 | price: 500,
30 | rating: 4.5,
31 | sold: 288,
32 | image: 'https://source.unsplash.com/dUx0gwLbhzs',
33 | },
34 | {
35 | id: 5,
36 | name: 'White Keyboard',
37 | price: 200,
38 | rating: 5.0,
39 | sold: 196,
40 | image: 'https://source.unsplash.com/Ac9L5MrIPUQ',
41 | },
42 | ];
43 |
--------------------------------------------------------------------------------
/src/dummies/users.js:
--------------------------------------------------------------------------------
1 | export const users = [
2 | {
3 | id: 1,
4 | avatar: `https://randomuser.me/api/portraits/women/4.jpg`,
5 | name: 'Bealle Frankland',
6 | email: 'bealles-frankland@example.com',
7 | role: 'customer',
8 | status: 'active',
9 | created_at: 'November 07, 2020',
10 | },
11 | {
12 | id: 2,
13 | avatar: `https://randomuser.me/api/portraits/women/8.jpg`,
14 | name: 'Kelsi Kohrt',
15 | email: 'kelsi-kohrt@example.com',
16 | role: 'customer',
17 | status: 'inactive',
18 | created_at: 'November 02, 2020',
19 | },
20 | {
21 | id: 3,
22 | avatar: `https://randomuser.me/api/portraits/men/12.jpg`,
23 | name: 'Denny Marfell',
24 | email: 'denny-marfell@example.com',
25 | role: 'customer',
26 | status: 'active',
27 | created_at: 'October 31, 2020',
28 | },
29 | {
30 | id: 4,
31 | avatar: `https://randomuser.me/api/portraits/women/16.jpg`,
32 | name: 'Dede Iowarch',
33 | email: 'dede-iowarch@example.com',
34 | role: 'customer',
35 | status: 'active',
36 | created_at: 'October 31, 2020',
37 | },
38 | {
39 | id: 5,
40 | avatar: `https://randomuser.me/api/portraits/women/20.jpg`,
41 | name: 'Carmelia Bushill',
42 | email: 'carmelia-bushill@example.com',
43 | role: 'customer',
44 | status: 'inactive',
45 | created_at: 'October 24, 2020',
46 | },
47 | {
48 | id: 6,
49 | avatar: `https://randomuser.me/api/portraits/men/24.jpg`,
50 | name: 'Afton Maior',
51 | email: 'afton-maior@example.com',
52 | role: 'admin',
53 | status: 'active',
54 | created_at: 'October 16, 2020',
55 | },
56 | {
57 | id: 7,
58 | avatar: `https://randomuser.me/api/portraits/women/28.jpg`,
59 | name: 'Eula Issacson',
60 | email: 'eula-issacson@example.com',
61 | role: 'customer',
62 | status: 'active',
63 | created_at: 'September 29, 2020',
64 | },
65 | {
66 | id: 8,
67 | avatar: `https://randomuser.me/api/portraits/women/32.jpg`,
68 | name: 'Javier Blabber',
69 | email: 'javier-blabber@example.com',
70 | role: 'admin',
71 | status: 'active',
72 | created_at: 'September 29, 2020',
73 | },
74 | {
75 | id: 9,
76 | avatar: `https://randomuser.me/api/portraits/men/36.jpg`,
77 | name: 'Nikolaos Ciotto',
78 | email: 'nikolaos-ciotto@example.com',
79 | role: 'customer',
80 | status: 'active',
81 | created_at: 'September 15, 2020',
82 | },
83 | {
84 | id: 10,
85 | avatar: `https://randomuser.me/api/portraits/men/40.jpg`,
86 | name: 'Delbert McIlharga',
87 | email: 'delbert-mcilharga@example.com',
88 | role: 'customer',
89 | status: 'inactive',
90 | created_at: 'August 21, 2020',
91 | },
92 | ];
93 |
94 | export const statuses = {
95 | active: 'green',
96 | inactive: 'red',
97 | };
98 |
--------------------------------------------------------------------------------
/src/layouts/AppLayout.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import BaseHead from '$components/BaseHead.astro';
3 | import Navbar from '$components/Navbar.astro';
4 | import Sidebar from '$components/Sidebar.astro';
5 |
6 | const { title, ...props } = Astro.props;
7 |
8 | const sidebarOptions = JSON.stringify({
9 | noScrollClass: 'overflow-y-hidden',
10 | noScrollBreakpoint: 1024,
11 | }).replace(/\"/g, "'");
12 | ---
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | {Boolean(title) &&
{title} }
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/layouts/AuthLayout.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import clsx from 'clsx';
3 | import BaseHead from '$components/BaseHead.astro';
4 | import Logo from '$components/Logo.astro';
5 |
6 | const { title, background, right = false } = Astro.props;
7 | ---
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
{title}
17 |
18 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/pages/404.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import BaseHead from '$components/BaseHead.astro';
3 | import Button from '$components/Button.astro';
4 | import Link from '$components/Link.astro';
5 | import Logo from '$components/Logo.astro';
6 | ---
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Oops! The page you're looking for is nowhere to be found
17 |
Back to dashboard
18 |
Or back to previous page
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/pages/_home/Overview.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import ContentSection from '$components/ContentSection.astro';
3 | import ContentSectionTitle from '$components/ContentSectionTitle.astro';
4 | import StatsCard from '$components/StatsCard.astro';
5 |
6 | const { className } = Astro.props;
7 |
8 | const stats = [
9 | {
10 | label: 'Account balance',
11 | value: '$ 21,853',
12 | icon: ,
13 | },
14 | {
15 | label: 'Total users',
16 | value: '1,279',
17 | icon: ,
18 | },
19 | {
20 | label: 'Listed products',
21 | value: '358',
22 | icon: ,
23 | },
24 | {
25 | label: 'Completed orders',
26 | value: '604',
27 | icon: ,
28 | },
29 | ];
30 | ---
31 |
32 |
33 | Overview
34 |
35 | {stats.map(({label, value, icon}) => (
36 |
37 | ))}
38 |
39 |
--------------------------------------------------------------------------------
/src/pages/_home/PopularProduct.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Avatar from '$components/Avatar.astro';
3 | import Card from '$components/Card.astro';
4 | import ContentSection from '$components/ContentSection.astro';
5 | import ContentSectionTitle from '$components/ContentSectionTitle.astro';
6 | import { products } from '$dummies/products.js';
7 |
8 | const { className } = Astro.props;
9 | ---
10 |
11 |
12 |
13 | Popular Products
14 | Popular period
15 |
16 | Monthly
17 | Weekly
18 | Daily
19 |
20 |
21 |
22 |
23 | {products.map(({ name, price, sold, rating, image }) => (
24 |
25 |
26 |
27 |
28 |
29 |
{name}
30 | $ {price} USD
31 |
32 |
33 |
34 | {sold} units sold
35 |
36 |
37 | {rating} / 5
38 |
39 |
40 |
41 |
42 |
43 |
44 | ))}
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/pages/_home/RecentOrder.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Avatar from '$components/Avatar.astro';
3 | import Badge from '$components/Badge.astro';
4 | import Card from '$components/Card.astro';
5 | import ContentSection from '$components/ContentSection.astro';
6 | import ContentSectionTitle from '$components/ContentSectionTitle.astro';
7 | import Link from '$components/Link.astro';
8 | import TBody from '$components/TBody.astro';
9 | import Td from '$components/Td.astro';
10 | import Th from '$components/Th.astro';
11 | import THead from '$components/THead.astro';
12 | import { orders, statuses } from '$dummies/orders.js';
13 |
14 | const { className } = Astro.props;
15 |
16 | const shownOrders = orders.slice(0, 5);
17 | ---
18 |
19 |
20 | Recent Orders
21 | View all
22 |
23 |
24 |
25 |
26 |
27 |
28 | Buyer
29 |
30 |
31 | Amount
32 |
33 |
34 | Status
35 |
36 |
37 | Date
38 |
39 |
40 |
41 |
42 | {shownOrders.map(({ buyer: {avatar, name, email}, amount, status, date }) => (
43 |
44 |
45 |
46 |
47 |
48 |
{name}
49 |
{email}
50 |
51 |
52 |
53 |
54 | $ {amount} USD
55 |
56 |
57 | {status}
58 |
59 |
60 | {date}
61 |
62 |
63 | ))}
64 |
65 |
66 |
67 |
68 | {shownOrders.map(({ buyer: {avatar, name, email}, amount, date, status }) => (
69 |
70 |
71 | {date}
72 | {status}
73 |
74 |
75 |
76 |
77 |
78 |
{name}
79 |
{email}
80 |
81 |
82 |
$ {amount} USD
83 |
84 |
85 | ))}
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/pages/auth/email-verification.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import AuthLayout from '$layouts/AuthLayout.astro';
3 | import Button from '$components/Button.astro';
4 | ---
5 |
6 |
7 |
8 |
Thanks for signing up!
9 |
Before getting started, could you verify your email address by clicking on the link we just emailed to you?
10 |
If you didn't receive the email, we will gladly send you another.
11 |
12 |
15 |
16 | Log out
17 |
18 |
--------------------------------------------------------------------------------
/src/pages/auth/forgot-password.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import AuthLayout from '$layouts/AuthLayout.astro';
3 | import Button from '$components/Button.astro';
4 | import Textbox from '$components/Textbox.astro';
5 | ---
6 |
7 |
8 |
9 |
Forgot your password? No problem.
10 |
Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.
11 |
12 |
22 |
--------------------------------------------------------------------------------
/src/pages/auth/login.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import AuthLayout from '$layouts/AuthLayout.astro';
3 | import Button from '$components/Button.astro';
4 | import Checkbox from '$components/Checkbox.astro';
5 | import Link from '$components/Link.astro';
6 | import Textbox from '$components/Textbox.astro';
7 | ---
8 |
9 |
10 |
36 |
37 | Not registered yet?
38 |
39 | Create an account
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/pages/auth/register.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import AuthLayout from '$layouts/AuthLayout.astro';
3 | import Button from '$components/Button.astro';
4 | import Link from '$components/Link.astro';
5 | import Textbox from '$components/Textbox.astro';
6 | ---
7 |
8 |
9 |
39 |
40 | Already registered?
41 |
42 | Login
43 |
44 |
45 |
--------------------------------------------------------------------------------
/src/pages/blank.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import AppLayout from '$layouts/AppLayout.astro';
3 | ---
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/pages/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import AppLayout from '$layouts/AppLayout.astro';
3 | import Overview from './_home/Overview.astro';
4 | import RecentOrder from './_home/RecentOrder.astro';
5 | import PopularProduct from './_home/PopularProduct.astro';
6 | ---
7 |
8 |
9 |
16 |
17 |
--------------------------------------------------------------------------------
/src/pages/profile/_components/BrowserSession.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Button from '$components/Button.astro';
3 | import ContentSection from '$components/ContentSection.astro';
4 | import ContentSectionTitle from '$components/ContentSectionTitle.astro';
5 | ---
6 |
7 |
8 |
Browser Sessions
9 |
10 | Manage and log out your active sessions on other browsers and devices.
11 |
12 |
13 |
14 | If necessary, you may log out of all of your other browser sessions across all of your devices.
15 |
16 |
17 | Some of your recent sessions are listed below; however, this list may not be exhaustive. If you feel your account has been compromised, you should also update your password.
18 |
19 | Log out other browser sessions
20 |
--------------------------------------------------------------------------------
/src/pages/profile/_components/ProfileInformation.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Button from '$components/Button.astro';
3 | import ContentSection from '$components/ContentSection.astro';
4 | import ContentSectionTitle from '$components/ContentSectionTitle.astro';
5 | import Textbox from '$components/Textbox.astro';
6 | ---
7 |
8 |
69 |
--------------------------------------------------------------------------------
/src/pages/profile/_components/UpdatePassword.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Button from '$components/Button.astro';
3 | import ContentSection from '$components/ContentSection.astro';
4 | import ContentSectionTitle from '$components/ContentSectionTitle.astro';
5 | import Textbox from '$components/Textbox.astro';
6 | ---
7 |
8 |
9 |
Update Password
10 |
11 | Ensure your account is using a long, random password to stay secure.
12 |
13 |
14 |
37 |
--------------------------------------------------------------------------------
/src/pages/profile/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import AppLayout from '$layouts/AppLayout.astro';
3 | import BrowserSession from './_components/BrowserSession.astro';
4 | import ProfileInformation from './_components/ProfileInformation.astro';
5 | import UpdatePassword from './_components/UpdatePassword.astro';
6 | ---
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/pages/users/_components/CreateUserDialog.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Alert from '$components/Alert.astro';
3 | import Button from '$components/Button.astro';
4 | import Dialog from '$components/Dialog.astro';
5 | import Link from '$components/Link.astro';
6 | import Textbox from '$components/Textbox.astro';
7 | ---
8 |
14 |
50 |
--------------------------------------------------------------------------------
/src/pages/users/_components/Filter.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Input from '$components/Input.astro';
3 | import Select from '$components/Select.astro';
4 | ---
5 |
--------------------------------------------------------------------------------
/src/pages/users/_components/Order.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Link from '$components/Link.astro';
3 | ---
4 |
8 |
9 | Order by
10 |
11 |
12 | registration date
13 |
14 |
15 | name
16 |
17 |
18 |
19 |
20 | from
21 |
22 |
23 |
24 | newest
25 |
26 |
27 | oldest
28 |
29 |
30 |
31 |
32 |
33 |
34 | A-Z
35 |
36 |
37 | Z-A
38 |
39 |
40 |
41 |
42 |
Reset
43 |
--------------------------------------------------------------------------------
/src/pages/users/_components/Table.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import Avatar from '$components/Avatar.astro';
3 | import Badge from '$components/Badge.astro';
4 | import Card from '$components/Card.astro';
5 | import IconButton from '$components/IconButton.astro';
6 | import Td from '$components/Td.astro';
7 | import TBody from '$components/TBody.astro';
8 | import Th from '$components/Th.astro';
9 | import THead from '$components/THead.astro';
10 | import { users, statuses } from '$dummies/users';
11 | ---
12 |
13 |
14 |
15 |
16 |
17 |
18 | Name
19 |
20 |
21 | Role
22 |
23 |
24 | Status
25 |
26 |
27 | Registration date
28 |
29 |
30 |
31 |
32 |
33 | {users.map(({avatar, name, email, role, status, created_at}) => (
34 |
35 |
36 |
37 |
38 |
39 |
{name}
40 |
{email}
41 |
42 |
43 |
44 | {role}
45 |
46 | {status}
47 |
48 |
49 | {created_at}
50 |
51 |
52 |
53 |
54 | Edit User
55 |
56 |
57 |
58 | ))}
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/src/pages/users/index.astro:
--------------------------------------------------------------------------------
1 | ---
2 | import AppLayout from '$layouts/AppLayout.astro';
3 | import Button from '$components/Button.astro';
4 | import DataCounter from '$components/DataCounter.astro';
5 | import Pagination from '$components/Pagination.astro';
6 | import SmallPagination from '$components/SmallPagination.astro';
7 | import CreateUserDialog from './_components/CreateUserDialog.astro';
8 | import Filter from './_components/Filter.astro';
9 | import Order from './_components/Order.astro';
10 | import Table from './_components/Table.astro';
11 | ---
12 |
13 |
18 | Create User
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/src/scripts/dialog.js:
--------------------------------------------------------------------------------
1 | export default function dialog() {
2 | return {
3 | id: Math.random().toString(36).substr(2, 9),
4 |
5 | dialog: {
6 | [':role']() {
7 | return 'dialog';
8 | },
9 | [':aria-modal']() {
10 | return true;
11 | },
12 | [':aria-labelledby']() {
13 | return `dialog-title-${this.id}`;
14 | }
15 | },
16 |
17 | dialogTitle: {
18 | [':id']() {
19 | return `dialog-title-${this.id}`;
20 | },
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/scripts/main.js:
--------------------------------------------------------------------------------
1 | import Alpine from 'alpinejs';
2 | import trap from '@alpinejs/trap';
3 | import collapse from '@alpinejs/collapse';
4 | import dialog from './dialog.js';
5 | import menu from './menu.js';
6 | import popover from './popover.js';
7 | import sidebar from './sidebar.js';
8 | import sidebarSub from './sidebarsub.js';
9 |
10 | Alpine.plugin(trap);
11 | Alpine.plugin(collapse);
12 |
13 | Alpine.data('dialog', dialog);
14 | Alpine.data('menu', menu);
15 | Alpine.data('popover', popover);
16 | Alpine.data('sidebar', sidebar);
17 | Alpine.data('sidebarSub', sidebarSub);
18 |
19 | Alpine.start();
20 |
--------------------------------------------------------------------------------
/src/scripts/menu.js:
--------------------------------------------------------------------------------
1 | export default function menu(menuItems = []) {
2 | return {
3 | id: Math.random().toString(36).substr(2, 9),
4 | open: false,
5 | active: null,
6 | menuItems: addIndexToEachMenuItem(menuItems),
7 | length: menuItems.flat().length,
8 |
9 | init() {
10 | this.$nextTick(() => {
11 | const menuItems = this.$refs.menuList.querySelectorAll('[role=menuitem]');
12 | this.$watch('active', value => {
13 | if (value !== null) {
14 | menuItems[value - 1].focus();
15 | } else {
16 | this.$refs.menuList.focus();
17 | }
18 | });
19 | });
20 | },
21 |
22 | close() {
23 | this.open = false;
24 | this.active = null;
25 | },
26 |
27 | toggle() {
28 | if (this.open) {
29 | this.active = null;
30 | this.$nextTick(() => {
31 | this.$refs.menuButton.focus();
32 | });
33 | }
34 |
35 | this.open = !this.open;
36 | },
37 |
38 | focusPreviousMenu() {
39 | if (this.active === null) {
40 | this.$refs.menuButton.blur();
41 | this.active = this.length;
42 | return;
43 | }
44 | if (this.active === 1) {
45 | return;
46 | }
47 | this.active -= 1;
48 | },
49 |
50 | focusNextMenu() {
51 | if (this.active === null) {
52 | this.$refs.menuButton.blur();
53 | this.active = 1;
54 | return;
55 | }
56 | if (this.active === this.length) {
57 | return;
58 | }
59 | this.active += 1;
60 | },
61 |
62 | menu: {
63 | ['@click.outside']() {
64 | this.close();
65 | },
66 | ['@keydown.escape']() {
67 | this.close();
68 | this.$nextTick(() => {
69 | this.$refs.menuButton.focus();
70 | });
71 | },
72 | },
73 |
74 | menuButton: {
75 | ['@click']() {
76 | this.toggle();
77 | this.$nextTick(() => {
78 | this.$refs.menuList.focus();
79 | });
80 | },
81 | ['@keydown.space']() {
82 | this.$nextTick(() => {
83 | this.$refs.menuList.querySelector('[role=menuitem]').focus();
84 | });
85 | },
86 | ['@keydown.enter']() {
87 | this.$nextTick(() => {
88 | this.$refs.menuList.querySelector('[role=menuitem]').focus();
89 | });
90 | },
91 | [':id']() {
92 | return `menu-button-${this.id}`;
93 | },
94 | [':aria-haspopup']() {
95 | return true;
96 | },
97 | [':aria-expanded']() {
98 | return this.open;
99 | },
100 | [':aria-controls']() {
101 | return `menu-items-${this.id}`;
102 | },
103 | },
104 |
105 | menuList: {
106 | ['x-show']() {
107 | return this.open;
108 | },
109 | ['@keydown.tab.prevent']() {},
110 | ['@keydown.arrow-up.prevent']() {
111 | this.focusPreviousMenu();
112 | },
113 | ['@keydown.arrow-down.prevent']() {
114 | this.focusNextMenu();
115 | },
116 | [':role']() {
117 | return 'menu';
118 | },
119 | [':id']() {
120 | return `menu-items-${this.id}`;
121 | },
122 | [':aria-labelledby']() {
123 | return `menu-button-${this.id}`;
124 | },
125 | [':tabindex']() {
126 | return 0;
127 | },
128 | },
129 |
130 | menuItem: {
131 | ['@keydown.tab.prevent']() {},
132 | ['@keydown.arrow-up.prevent.stop']() {
133 | this.focusPreviousMenu();
134 | },
135 | ['@keydown.arrow-down.prevent.stop']() {
136 | this.focusNextMenu();
137 | },
138 | ['@mouseleave']() {
139 | this.active = null;
140 | },
141 | ['@click']() {
142 | this.close();
143 | },
144 | [':role']() {
145 | return 'menuitem';
146 | },
147 | [':tabindex']() {
148 | return -1;
149 | },
150 | },
151 | };
152 | }
153 |
154 | const addIndexToEachMenuItem = menuItems => {
155 | let index = 0;
156 |
157 | const flatMenuItemsWithArrayIndex = menuItems.flatMap((menuItem, arrIndex) => {
158 | return menuItem.map(item => {
159 | index++;
160 | return {
161 | ...item,
162 | arrIndex,
163 | index,
164 | };
165 | });
166 | });
167 |
168 | return flatMenuItemsWithArrayIndex.reduce((acc, item) => {
169 | const { arrIndex } = item;
170 | if (!acc[arrIndex]) {
171 | acc[arrIndex] = [];
172 | }
173 | acc[arrIndex].push(item);
174 | return acc;
175 | }, []);
176 | };
177 |
--------------------------------------------------------------------------------
/src/scripts/popover.js:
--------------------------------------------------------------------------------
1 | export default function popover() {
2 | return {
3 | id: Math.random().toString(36).substr(2, 9),
4 | open: false,
5 |
6 | close() {
7 | this.open = false;
8 | },
9 |
10 | toggle() {
11 | if (this.open) {
12 | this.active = null;
13 | this.$nextTick(() => {
14 | this.$refs.popoverButton.focus();
15 | });
16 | }
17 |
18 | this.open = !this.open;
19 | },
20 |
21 | popover: {
22 | ['x-trap']() {
23 | return this.open;
24 | },
25 | ['@keydown.escape']() {
26 | this.close();
27 | },
28 | ['@click.outside']() {
29 | this.close();
30 | },
31 | },
32 |
33 | popoverButton: {
34 | ['@click']() {
35 | this.toggle();
36 | },
37 | [':id']() {
38 | return `popover-button-${this.id}`;
39 | },
40 | [':aria-expanded']() {
41 | return this.open;
42 | },
43 | [':aria-controls']() {
44 | return `popover-panel-${this.id}`;
45 | },
46 | },
47 |
48 | popoverPanel: {
49 | ['x-show']() {
50 | return this.open;
51 | },
52 | [':id']() {
53 | return `popover-panel-${this.id}`;
54 | },
55 | },
56 | };
57 | }
58 |
--------------------------------------------------------------------------------
/src/scripts/sidebar.js:
--------------------------------------------------------------------------------
1 | export default function sidebar(
2 | options = { noScroll: false, noScrollClass: '', noScrollBreakpoint: 0 }
3 | ) {
4 | const { noScroll, noScrollClass, noScrollBreakpoint } = options;
5 | const isLowerThanScrollBreakpoint = () => window.innerWidth < noScrollBreakpoint;
6 |
7 | return {
8 | open: true,
9 | trap: false,
10 |
11 | init() {
12 | this.$nextTick(() => {
13 | if (isLowerThanScrollBreakpoint()) {
14 | this.open = false;
15 | }
16 | this.$watch('open', () => {
17 | if (this.open) {
18 | document.documentElement.classList.add(noScrollClass);
19 | } else {
20 | document.documentElement.classList.remove(noScrollClass);
21 | }
22 | });
23 | });
24 | },
25 |
26 | toggle() {
27 | if (!this.open) {
28 | this.$nextTick(() => {
29 | if (isLowerThanScrollBreakpoint()) {
30 | this.trap = true;
31 | }
32 | // TODO: remove this hack when alpine fixes the bug
33 | setTimeout(() => {
34 | this.$refs.sidebarMenu.querySelector('button:not([x-bind="sidebarClose"]), [href], input, select, textarea').focus();
35 | }, 50);
36 | });
37 | }
38 |
39 | this.open = !this.open;
40 | },
41 |
42 | close() {
43 | this.open = false;
44 | this.trap = false;
45 | this.$refs.sidebarButton.focus();
46 | },
47 |
48 | sidebarButton: {
49 | ['@click']() {
50 | this.toggle();
51 | },
52 | [':aria-controls']() {
53 | return 'sidebar';
54 | },
55 | [':aria-expanded']() {
56 | return this.open;
57 | },
58 | },
59 |
60 | sidebarClose: {
61 | ['@click']() {
62 | this.close();
63 | },
64 | },
65 |
66 | sidebarMenu: {
67 | ['x-show']() {
68 | return this.open;
69 | },
70 | [noScroll ? 'x-trap.noscroll' : 'x-trap']() {
71 | return this.trap;
72 | },
73 | ['@keydown.escape.document']() {
74 | if (isLowerThanScrollBreakpoint()) {
75 | this.close();
76 | }
77 | },
78 | ['@resize.window']() {
79 | if (isLowerThanScrollBreakpoint()) {
80 | this.trap = true;
81 | } else {
82 | this.trap = false;
83 | }
84 | },
85 | [':id']() {
86 | return 'sidebar';
87 | },
88 | },
89 |
90 | sidebarOverlay: {
91 | ['x-show']() {
92 | return this.open;
93 | },
94 | ['@click']() {
95 | this.close();
96 | },
97 | },
98 | };
99 | }
100 |
--------------------------------------------------------------------------------
/src/scripts/sidebarsub.js:
--------------------------------------------------------------------------------
1 | export default function sidebarSub(initialOpen) {
2 | return {
3 | id: Math.random().toString(36).substr(2, 9),
4 | open: initialOpen,
5 |
6 | toggle() {
7 | this.open = !this.open;
8 | },
9 |
10 | sidebarSubButton: {
11 | ['@click']() {
12 | this.toggle();
13 | },
14 | [':aria-controls']() {
15 | return `sidebar-sub-menu-${this.id}`;
16 | },
17 | [':aria-expanded']() {
18 | return this.open;
19 | },
20 | },
21 |
22 | sidebarSubMenu: {
23 | ['x-show']() {
24 | return this.open;
25 | },
26 | // TODO: uncomment below code after alpine fix focus bug
27 | // ['x-collapse']() {},
28 | [':id']() {
29 | return `sidebar-sub-menu-${this.id}`;
30 | },
31 | },
32 | };
33 | }
34 |
--------------------------------------------------------------------------------
/src/styles/global.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | html {
7 | scroll-behavior: smooth;
8 | }
9 |
10 | @font-face {
11 | font-family: 'Plus Jakarta Sans';
12 | font-style: normal;
13 | font-weight: 200;
14 | font-display: swap;
15 | src: url('/fonts/PlusJakartaSans-ExtraLight.woff2') format('woff2'),
16 | url('/fonts/PlusJakartaSans-ExtraLight.woff') format('woff');
17 | }
18 |
19 | @font-face {
20 | font-family: 'Plus Jakarta Sans';
21 | font-style: italic;
22 | font-weight: 200;
23 | font-display: swap;
24 | src: url('/fonts/PlusJakartaSans-ExtraLightItalic.woff2') format('woff2'),
25 | url('/fonts/PlusJakartaSans-ExtraLightItalic.woff') format('woff');
26 | }
27 |
28 | @font-face {
29 | font-family: 'Plus Jakarta Sans';
30 | font-style: normal;
31 | font-weight: 300;
32 | font-display: swap;
33 | src: url('/fonts/PlusJakartaSans-Light.woff2') format('woff2'),
34 | url('/fonts/PlusJakartaSans-Light.woff') format('woff');
35 | }
36 |
37 | @font-face {
38 | font-family: 'Plus Jakarta Sans';
39 | font-style: italic;
40 | font-weight: 300;
41 | font-display: swap;
42 | src: url('/fonts/PlusJakartaSans-LightItalic.woff2') format('woff2'),
43 | url('/fonts/PlusJakartaSans-LightItalic.woff') format('woff');
44 | }
45 |
46 | @font-face {
47 | font-family: 'Plus Jakarta Sans';
48 | font-style: normal;
49 | font-weight: 400;
50 | font-display: swap;
51 | src: url('/fonts/PlusJakartaSans-Regular.woff2') format('woff2'),
52 | url('/fonts/PlusJakartaSans-Regular.woff') format('woff');
53 | }
54 |
55 | @font-face {
56 | font-family: 'Plus Jakarta Sans';
57 | font-style: italic;
58 | font-weight: 400;
59 | font-display: swap;
60 | src: url('/fonts/PlusJakartaSans-RegularItalic.woff2') format('woff2'),
61 | url('/fonts/PlusJakartaSans-RegularItalic.woff') format('woff');
62 | }
63 |
64 | @font-face {
65 | font-family: 'Plus Jakarta Sans';
66 | font-style: normal;
67 | font-weight: 500;
68 | font-display: swap;
69 | src: url('/fonts/PlusJakartaSans-Medium.woff2') format('woff2'),
70 | url('/fonts/PlusJakartaSans-Medium.woff') format('woff');
71 | }
72 |
73 | @font-face {
74 | font-family: 'Plus Jakarta Sans';
75 | font-style: italic;
76 | font-weight: 500;
77 | font-display: swap;
78 | src: url('/fonts/PlusJakartaSans-MediumItalic.woff2') format('woff2'),
79 | url('/fonts/PlusJakartaSans-MediumItalic.woff') format('woff');
80 | }
81 |
82 | @font-face {
83 | font-family: 'Plus Jakarta Sans';
84 | font-style: normal;
85 | font-weight: 600;
86 | font-display: swap;
87 | src: url('/fonts/PlusJakartaSans-SemiBold.woff2') format('woff2'),
88 | url('/fonts/PlusJakartaSans-SemiBold.woff') format('woff');
89 | }
90 |
91 | @font-face {
92 | font-family: 'Plus Jakarta Sans';
93 | font-style: italic;
94 | font-weight: 600;
95 | font-display: swap;
96 | src: url('/fonts/PlusJakartaSans-SemiBoldItalic.woff2') format('woff2'),
97 | url('/fonts/PlusJakartaSans-SemiBoldItalic.woff') format('woff');
98 | }
99 |
100 | @font-face {
101 | font-family: 'Plus Jakarta Sans';
102 | font-style: normal;
103 | font-weight: 700;
104 | font-display: swap;
105 | src: url('/fonts/PlusJakartaSans-Bold.woff2') format('woff2'),
106 | url('/fonts/PlusJakartaSans-Bold.woff') format('woff');
107 | }
108 |
109 | @font-face {
110 | font-family: 'Plus Jakarta Sans';
111 | font-style: italic;
112 | font-weight: 700;
113 | font-display: swap;
114 | src: url('/fonts/PlusJakartaSans-BoldItalic.woff2') format('woff2'),
115 | url('/fonts/PlusJakartaSans-BoldItalic.woff') format('woff');
116 | }
117 |
118 | @font-face {
119 | font-family: 'Plus Jakarta Sans';
120 | font-style: normal;
121 | font-weight: 800;
122 | font-display: swap;
123 | src: url('/fonts/PlusJakartaSans-ExtraBold.woff2') format('woff2'),
124 | url('/fonts/PlusJakartaSans-ExtraBold.woff') format('woff');
125 | }
126 |
127 | @font-face {
128 | font-family: 'Plus Jakarta Sans';
129 | font-style: italic;
130 | font-weight: 800;
131 | font-display: swap;
132 | src: url('/fonts/PlusJakartaSans-ExtraBoldItalic.woff2') format('woff2'),
133 | url('/fonts/PlusJakartaSans-ExtraBoldItalic.woff') format('woff');
134 | }
135 |
136 | [type='checkbox']:checked {
137 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23FFFFFF' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12l6 6L20 6'/%3E%3C/svg%3E");
138 | background-size: 0.75rem;
139 | @apply bg-center bg-no-repeat;
140 | }
141 |
142 | select:not(.reset) {
143 | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236B7280%0A' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 17l4 4 4-4'/%3E%3Cpath d='M8 7l4-4 4 4'/%3E%3C/svg%3E");
144 | background-position: right 1rem center;
145 | background-repeat: no-repeat;
146 | background-size: 1rem;
147 | }
148 |
149 | [x-cloak] {
150 | @apply hidden;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const defaultTheme = require('tailwindcss/defaultTheme');
2 | const colors = require('tailwindcss/colors');
3 |
4 | module.exports = {
5 | content: ['./public/**/*.html', './src/**/*.{astro,js,jsx,svelte,ts,tsx,vue}'],
6 | theme: {
7 | extend: {
8 | fontFamily: {
9 | sans: ['Plus Jakarta Sans', ...defaultTheme.fontFamily.sans],
10 | },
11 | colors: {
12 | current: 'currentColor',
13 | primary: colors.blue,
14 | success: colors.emerald,
15 | danger: colors.rose,
16 | warning: colors.amber,
17 | info: colors.indigo,
18 | },
19 | },
20 | },
21 | plugins: [],
22 | };
23 |
--------------------------------------------------------------------------------