├── public ├── _redirects └── favicon.png ├── .prettierrc ├── src ├── vite-env.d.ts ├── assets │ ├── fonts │ │ ├── Akira.ttf │ │ └── Redro.ttf │ ├── images │ │ ├── eyes.gif │ │ ├── moth.png │ │ ├── mask.webp │ │ ├── shapes.gif │ │ ├── snake.png │ │ ├── butterfly.png │ │ ├── owl-head.webp │ │ ├── painting.png │ │ ├── butterfly-2.png │ │ ├── carousel-1.gif │ │ ├── carousel-2.gif │ │ ├── carousel-3.gif │ │ ├── moon-phases.png │ │ ├── thinking-man.webp │ │ ├── zodiac-wheel.png │ │ ├── download-android.svg │ │ └── download-apple.svg │ └── printscreens │ │ ├── laptop.png │ │ ├── mobile.png │ │ ├── tablet.png │ │ ├── dark-mode.png │ │ └── light-mode.png ├── components │ ├── Pagination │ │ ├── Pagination.css │ │ ├── Pagination.types.ts │ │ └── Pagination.tsx │ ├── Accordion │ │ ├── Accordion.types.ts │ │ ├── Accordion.tsx │ │ └── Accordion.css │ ├── Button │ │ ├── Button.types.ts │ │ ├── Button.tsx │ │ └── Button.css │ ├── Loading │ │ ├── Loading.tsx │ │ └── Loading.css │ ├── MagicCard │ │ ├── MagicCard.css │ │ └── MagicCard.tsx │ ├── ZodiacCard │ │ ├── ZodiacCard.tsx │ │ └── ZodiacCard.css │ ├── CardReading │ │ ├── CardReading.css │ │ └── CardReading.tsx │ ├── ThemeToggle │ │ ├── ThemeToggle.tsx │ │ └── ThemeToggle.css │ ├── SubscribeForm │ │ ├── SubscribeForm.css │ │ └── SubscribeForm.tsx │ ├── FlipCard │ │ ├── FlipCard.tsx │ │ └── FlipCard.css │ ├── ImageCarousel │ │ ├── ImageCarousel.css │ │ └── ImageCarousel.tsx │ ├── Footer │ │ ├── Footer.css │ │ └── Footer.tsx │ └── Header │ │ ├── Header.tsx │ │ └── Header.css ├── pages │ ├── Numerology │ │ ├── Numerology.types.ts │ │ ├── Numerology.css │ │ └── Numerology.tsx │ ├── NotFound │ │ ├── NotFound.css │ │ └── NotFound.tsx │ ├── About │ │ ├── About.css │ │ └── About.tsx │ ├── ZodiacSignDetails │ │ ├── ZodiacSignDetails.css │ │ └── ZodiacSignDetails.tsx │ ├── Zodiac │ │ ├── Zodiac.css │ │ └── Zodiac.tsx │ ├── Tarot │ │ ├── Tarot.css │ │ └── Tarot.tsx │ └── Home │ │ ├── Home.tsx │ │ └── Home.css ├── App.css ├── styles │ ├── global │ │ ├── animation-variables.css │ │ ├── space-variables.css │ │ ├── base.css │ │ ├── color-variables.css │ │ └── typography-variables.css │ ├── utilities │ │ ├── class-utilities.css │ │ └── animation-utilities.css │ ├── index.css │ └── resets │ │ └── normalize.css ├── types │ ├── Tarot.types.ts │ └── Zodiac.types.ts ├── main.tsx ├── hooks │ └── useFetch.ts ├── data │ ├── lettersData.ts │ ├── accordionData.ts │ └── lifePathData.ts └── App.tsx ├── vite.config.ts ├── tsconfig.node.json ├── .gitignore ├── .eslintrc.cjs ├── index.html ├── tsconfig.json ├── package.json └── README.md /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/public/favicon.png -------------------------------------------------------------------------------- /src/assets/fonts/Akira.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/fonts/Akira.ttf -------------------------------------------------------------------------------- /src/assets/fonts/Redro.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/fonts/Redro.ttf -------------------------------------------------------------------------------- /src/assets/images/eyes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/eyes.gif -------------------------------------------------------------------------------- /src/assets/images/moth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/moth.png -------------------------------------------------------------------------------- /src/assets/images/mask.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/mask.webp -------------------------------------------------------------------------------- /src/assets/images/shapes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/shapes.gif -------------------------------------------------------------------------------- /src/assets/images/snake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/snake.png -------------------------------------------------------------------------------- /src/assets/images/butterfly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/butterfly.png -------------------------------------------------------------------------------- /src/assets/images/owl-head.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/owl-head.webp -------------------------------------------------------------------------------- /src/assets/images/painting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/painting.png -------------------------------------------------------------------------------- /src/assets/images/butterfly-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/butterfly-2.png -------------------------------------------------------------------------------- /src/assets/images/carousel-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/carousel-1.gif -------------------------------------------------------------------------------- /src/assets/images/carousel-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/carousel-2.gif -------------------------------------------------------------------------------- /src/assets/images/carousel-3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/carousel-3.gif -------------------------------------------------------------------------------- /src/assets/images/moon-phases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/moon-phases.png -------------------------------------------------------------------------------- /src/assets/images/thinking-man.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/thinking-man.webp -------------------------------------------------------------------------------- /src/assets/images/zodiac-wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/images/zodiac-wheel.png -------------------------------------------------------------------------------- /src/assets/printscreens/laptop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/printscreens/laptop.png -------------------------------------------------------------------------------- /src/assets/printscreens/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/printscreens/mobile.png -------------------------------------------------------------------------------- /src/assets/printscreens/tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/printscreens/tablet.png -------------------------------------------------------------------------------- /src/assets/printscreens/dark-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/printscreens/dark-mode.png -------------------------------------------------------------------------------- /src/assets/printscreens/light-mode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxenia/astrology-app/HEAD/src/assets/printscreens/light-mode.png -------------------------------------------------------------------------------- /src/components/Pagination/Pagination.css: -------------------------------------------------------------------------------- 1 | .pagination { 2 | display: flex; 3 | flex-direction: row; 4 | justify-content: center; 5 | } 6 | -------------------------------------------------------------------------------- /src/pages/Numerology/Numerology.types.ts: -------------------------------------------------------------------------------- 1 | export interface LifePathProps { 2 | number: number; 3 | title: string; 4 | description: string; 5 | url: string; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Accordion/Accordion.types.ts: -------------------------------------------------------------------------------- 1 | export interface AccordionProps { 2 | title: string; 3 | content: string; 4 | isActive: boolean; 5 | onToggle: () => void; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/Pagination/Pagination.types.ts: -------------------------------------------------------------------------------- 1 | export interface PaginationProps { 2 | page: number; 3 | totalPages: number; 4 | handlePagination: (page: number) => void; 5 | } 6 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | #dark { 2 | background-color: #000000; 3 | color: rgba(255, 255, 255, 0.834); 4 | transition: 0.3s all; 5 | } 6 | 7 | #light { 8 | transition: 0.3s all; 9 | } 10 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /src/pages/NotFound/NotFound.css: -------------------------------------------------------------------------------- 1 | .not-found { 2 | height: 100vh; 3 | display: flex; 4 | justify-content: center; 5 | flex-direction: column; 6 | align-items: center; 7 | margin: auto; 8 | width: 50%; 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/styles/global/animation-variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --duration-instant: 0ms; 3 | --duration-fast: 50ms; 4 | --duration-quick: 100ms; 5 | --duration-standard: 150ms; 6 | --duration-slow: 250ms; 7 | --duration-slower: 300ms; 8 | --duration-slowest: 600ms; 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Button/Button.types.ts: -------------------------------------------------------------------------------- 1 | export interface ButtonProps { 2 | children: string; 3 | url?: string; 4 | target?: '_blank' | '_self'; 5 | className?: string; 6 | onClick?: () => void; 7 | as?: 'button' | 'link'; 8 | type?: 'button' | 'submit' | 'reset'; 9 | } 10 | -------------------------------------------------------------------------------- /src/types/Tarot.types.ts: -------------------------------------------------------------------------------- 1 | export interface TarotProps { 2 | id: number; 3 | image: string; 4 | link: string; 5 | name: string; 6 | reversed: string[]; 7 | upright: string[]; 8 | type: 'Major' | 'Minor'; 9 | } 10 | 11 | export interface TarotCardProps { 12 | card: TarotProps; 13 | } 14 | -------------------------------------------------------------------------------- /src/styles/global/space-variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --space-005: 0.05rem; 3 | --space-02: 0.2rem; 4 | --space-03: 0.3rem; 5 | --space-04: 0.4rem; 6 | --space-05: 0.5rem; 7 | --space-10: 1rem; 8 | --space-15: 1.5rem; 9 | --space-20: 2rem; 10 | --space-25: 25rem; 11 | --space-30: 3rem; 12 | --space-40: 4rem; 13 | } 14 | -------------------------------------------------------------------------------- /src/styles/utilities/class-utilities.css: -------------------------------------------------------------------------------- 1 | [data-theme='dark'] .invert { 2 | -webkit-filter: invert(80%); 3 | filter: invert(80%); 4 | } 5 | 6 | .description { 7 | width: 90%; 8 | margin: auto; 9 | text-align: start; 10 | } 11 | 12 | @media (min-width: 1200px) { 13 | .description { 14 | width: 60%; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/styles/index.css: -------------------------------------------------------------------------------- 1 | @import url('./resets/normalize.css'); 2 | 3 | @import './global/animation-variables.css'; 4 | @import './global/base.css'; 5 | @import './global/color-variables.css'; 6 | @import './global/space-variables.css'; 7 | @import './global/typography-variables.css'; 8 | 9 | @import './utilities/animation-utilities.css'; 10 | @import './utilities/class-utilities.css'; 11 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import { BrowserRouter } from 'react-router-dom'; 4 | 5 | import App from './App.tsx'; 6 | 7 | import './styles/index.css'; 8 | 9 | ReactDOM.createRoot(document.getElementById('root')!).render( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # Extra 27 | src/data-backup/ 28 | .prettierrc 29 | 30 | -------------------------------------------------------------------------------- /src/pages/NotFound/NotFound.tsx: -------------------------------------------------------------------------------- 1 | import mask from '../../assets/images/mask.webp'; 2 | import './NotFound.css'; 3 | 4 | function NotFound() { 5 | return ( 6 |
7 |

OOPS!

8 |

9 | Something went wrong. The page you are looking for seems to not be here. 10 |

11 | 12 |
13 | ); 14 | } 15 | 16 | export default NotFound; 17 | -------------------------------------------------------------------------------- /src/components/Loading/Loading.tsx: -------------------------------------------------------------------------------- 1 | import './Loading.css'; 2 | 3 | function Loading() { 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | ); 18 | } 19 | 20 | export default Loading; 21 | -------------------------------------------------------------------------------- /src/pages/About/About.css: -------------------------------------------------------------------------------- 1 | .about { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | min-height: 100vh; 7 | } 8 | 9 | .about__info { 10 | width: 90%; 11 | text-align: start; 12 | } 13 | 14 | .about img { 15 | width: 15rem; 16 | } 17 | 18 | .about { 19 | padding-bottom: var(--space-40); 20 | } 21 | 22 | @media (min-width: 768px) { 23 | .about__info { 24 | text-align: center; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | Lunar | Astrology in your pocket 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/components/MagicCard/MagicCard.css: -------------------------------------------------------------------------------- 1 | .glow { 2 | background: linear-gradient(-45deg, #ffe550, #e7a53c, #d55e23, #f59928); 3 | background-size: 400% 400%; 4 | animation: gradient 5s ease infinite; 5 | } 6 | 7 | html[data-theme='dark'] .glow { 8 | background: linear-gradient(-45deg, #8952ee, #6c00b5, #3823d5, #be28f5); 9 | background-size: 400% 400%; 10 | animation: gradient 5s ease infinite; 11 | } 12 | 13 | .magic-card { 14 | display: flex; 15 | justify-content: center; 16 | align-items: center; 17 | font-size: 200px; 18 | color: var(--white-sheer); 19 | } 20 | -------------------------------------------------------------------------------- /src/components/ZodiacCard/ZodiacCard.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | 3 | import type { ZodiacCardProps } from '../../types/Zodiac.types'; 4 | 5 | import './ZodiacCard.css'; 6 | 7 | function ZodiacCard({ sign }: ZodiacCardProps) { 8 | return ( 9 | 10 |
11 |

{sign.name}

12 | {sign.name} 13 | Dates: {sign.dates} 14 |
15 | 16 | ); 17 | } 18 | 19 | export default ZodiacCard; 20 | -------------------------------------------------------------------------------- /src/types/Zodiac.types.ts: -------------------------------------------------------------------------------- 1 | export interface ZodiacProps { 2 | id: number; 3 | elementId: number; 4 | modalityId: number; 5 | rulerId: number; 6 | name: string; 7 | dates: string; 8 | positiveTraits: string[]; 9 | negativeTraits: string[]; 10 | image: string; 11 | element: { image: string; keywords: string[]; name: string }; 12 | ruler: { 13 | image: string; 14 | keywords: string[]; 15 | name: string; 16 | transition: string; 17 | type: string; 18 | }; 19 | modality: { image: string; keywords: string[]; name: string }; 20 | } 21 | 22 | export interface ZodiacCardProps { 23 | sign: ZodiacProps; 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Accordion/Accordion.tsx: -------------------------------------------------------------------------------- 1 | import { AccordionProps } from './Accordion.types'; 2 | 3 | import './Accordion.css'; 4 | 5 | const Accordion = ({ title, content, isActive, onToggle }: AccordionProps) => { 6 | const handleClick = () => { 7 | onToggle(); 8 | }; 9 | 10 | return ( 11 |
12 |
13 |

{title}

14 |
{isActive ? '-' : '+'}
15 |
16 |

17 | {content} 18 |

19 |
20 | ); 21 | }; 22 | 23 | export default Accordion; 24 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /src/components/ZodiacCard/ZodiacCard.css: -------------------------------------------------------------------------------- 1 | .card__link { 2 | letter-spacing: 0; 3 | } 4 | 5 | .card__item { 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: center; 9 | align-items: center; 10 | font-weight: var(--medium-weight); 11 | gap: var(--space-10); 12 | } 13 | 14 | .card__item span { 15 | background-color: var(--quaternary-foreground); 16 | padding: var(--space-02); 17 | } 18 | 19 | .card__img { 20 | height: 8rem; 21 | } 22 | 23 | [data-theme='dark'] .card__img { 24 | -webkit-filter: invert(80%); 25 | filter: invert(80%); 26 | } 27 | 28 | @media (min-width: 768px) { 29 | .card__img { 30 | height: 6rem; 31 | } 32 | } 33 | 34 | @media (min-width: 992px) { 35 | .card__img { 36 | height: 8rem; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/CardReading/CardReading.css: -------------------------------------------------------------------------------- 1 | .reading--description { 2 | width: 70%; 3 | text-align: start; 4 | margin: auto; 5 | padding-bottom: var(--space-20); 6 | } 7 | 8 | .card__list--reading { 9 | display: grid; 10 | grid-template-columns: repeat(1, 1fr); 11 | gap: var(--space-10); 12 | margin: auto; 13 | padding: var(--space-20); 14 | } 15 | 16 | .card__btn--reading { 17 | display: block; 18 | margin: var(--space-20) auto; 19 | } 20 | 21 | @media (min-width: 920px) { 22 | .card__list--reading { 23 | grid-template-columns: repeat(3, 1fr); 24 | width: 90%; 25 | } 26 | } 27 | 28 | @media (min-width: 1200px) { 29 | .card__list--reading { 30 | width: 70%; 31 | } 32 | } 33 | 34 | @media (min-width: 1600px) { 35 | .card__list--reading { 36 | width: 50%; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/Button/Button.tsx: -------------------------------------------------------------------------------- 1 | import { ButtonProps } from './Button.types'; 2 | 3 | import './Button.css'; 4 | 5 | const Button = ({ 6 | children, 7 | url, 8 | target, 9 | className, 10 | onClick, 11 | type, 12 | as = 'button', 13 | }: ButtonProps) => { 14 | if (as === 'link' && url) { 15 | return ( 16 | 22 | {children} 23 | 24 | ); 25 | } else { 26 | return ( 27 | 34 | ); 35 | } 36 | }; 37 | 38 | export default Button; 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lunar", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^6.15.0" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.15", 19 | "@types/react-dom": "^18.2.7", 20 | "@typescript-eslint/eslint-plugin": "^6.0.0", 21 | "@typescript-eslint/parser": "^6.0.0", 22 | "@vitejs/plugin-react": "^4.0.3", 23 | "eslint": "^8.45.0", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.3", 26 | "typescript": "^5.0.2", 27 | "vite": "^4.5.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/pages/ZodiacSignDetails/ZodiacSignDetails.css: -------------------------------------------------------------------------------- 1 | .card-details { 2 | width: 80%; 3 | margin: auto; 4 | margin-bottom: var(--space-20); 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: flex-start; 8 | align-items: center; 9 | text-align: left; 10 | } 11 | 12 | .card-details__heading { 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | margin-bottom: var(--space-20); 17 | } 18 | 19 | .card-details__heading img { 20 | width: 5rem; 21 | margin-top: -2rem; 22 | margin-bottom: var(--space-10); 23 | } 24 | 25 | .card-details h3 { 26 | margin-top: var(--space-20); 27 | } 28 | 29 | .card-details ul { 30 | margin-bottom: var(--space-20); 31 | border: 2px solid var(--primary-foreground); 32 | padding: var(--space-20); 33 | box-shadow: var(--quaternary-foreground) 5px 5px; 34 | } 35 | 36 | .snake-img { 37 | width: 10rem; 38 | margin: var(--space-20); 39 | } 40 | -------------------------------------------------------------------------------- /src/styles/utilities/animation-utilities.css: -------------------------------------------------------------------------------- 1 | @keyframes skew-y-shaking { 2 | 0% { 3 | transform: translate(0, 0) rotate(0deg); 4 | } 5 | 25% { 6 | transform: translate(5px, 5px) rotate(5deg); 7 | } 8 | 50% { 9 | transform: translate(0, 0) rotate(0eg); 10 | } 11 | 75% { 12 | transform: translate(-5px, 5px) rotate(-5deg); 13 | } 14 | 100% { 15 | transform: translate(0, 0) rotate(0deg); 16 | } 17 | } 18 | 19 | @keyframes rotation { 20 | from { 21 | transform: rotate(0deg); 22 | } 23 | to { 24 | transform: rotate(359deg); 25 | } 26 | } 27 | 28 | @keyframes lds-roller { 29 | 0% { 30 | transform: rotate(0deg); 31 | } 32 | 100% { 33 | transform: rotate(360deg); 34 | } 35 | } 36 | 37 | @keyframes gradient { 38 | 0% { 39 | background-position: 0% 50%; 40 | } 41 | 50% { 42 | background-position: 100% 50%; 43 | } 44 | 100% { 45 | background-position: 0% 50%; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/components/ThemeToggle/ThemeToggle.tsx: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | 3 | import { ThemeContext } from '../../App'; 4 | 5 | import './ThemeToggle.css'; 6 | 7 | export default function ThemeToggle() { 8 | const { theme, toggleTheme } = useContext(ThemeContext); 9 | 10 | function onChangeHandler() { 11 | if (theme === 'light') { 12 | document.documentElement.setAttribute('data-theme', 'dark'); 13 | toggleTheme(); 14 | } else { 15 | document.documentElement.setAttribute('data-theme', 'light'); 16 | toggleTheme(); 17 | } 18 | } 19 | 20 | return ( 21 |
22 | 31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /src/components/SubscribeForm/SubscribeForm.css: -------------------------------------------------------------------------------- 1 | .subscribe-form { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | padding: 1rem; 7 | color: var(--tertiary-foreground); 8 | gap: 1rem; 9 | } 10 | 11 | .subscribe-form label { 12 | color: var(--tertiary-foreground); 13 | } 14 | 15 | .subscribe-form input { 16 | border: 2px solid var(--tertiary-foreground); 17 | background-color: var(--tertiary-background); 18 | color: var(--tertiary-foreground); 19 | padding: 0.5rem; 20 | margin: 0.5rem; 21 | width: 15rem; 22 | } 23 | 24 | .subscribe-form__button { 25 | color: var(--tertiary-foreground); 26 | background-color: var(--tertiary-background); 27 | border: 2px solid var(--tertiary-foreground); 28 | } 29 | 30 | @media (min-width: 768px) { 31 | .subscribe-form input { 32 | width: 20rem; 33 | } 34 | } 35 | 36 | @media (min-width: 1200px) { 37 | .subscribe-form input { 38 | width: 25rem; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/hooks/useFetch.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | const useFetch = (url: string) => { 4 | const [data, setData] = useState(null); 5 | const [error, setError] = useState(null); 6 | const [loading, setLoading] = useState(false); 7 | 8 | useEffect(() => { 9 | (async function () { 10 | const controller = new AbortController(); 11 | const signal = controller.signal; 12 | 13 | try { 14 | setLoading(true); 15 | 16 | const response = await fetch(url, { signal }); 17 | const data = await response.json(); 18 | 19 | setData(data); 20 | } catch (error) { 21 | setError(error as Error); 22 | } finally { 23 | setTimeout(() => { 24 | setLoading(false); 25 | }, 700); 26 | } 27 | 28 | return () => controller?.abort(); 29 | })(); 30 | }, [url]); 31 | 32 | return { data, error, loading }; 33 | }; 34 | 35 | export default useFetch; 36 | -------------------------------------------------------------------------------- /src/styles/global/base.css: -------------------------------------------------------------------------------- 1 | html { 2 | background-color: var(--primary-background); 3 | } 4 | 5 | body { 6 | font-family: var(--body-font); 7 | font-weight: var(--regular-weight); 8 | font-size: var(--regular-size); 9 | color: var(--primary-foreground); 10 | letter-spacing: var(--space-005); 11 | font-size: var(--regular-size); 12 | } 13 | 14 | button { 15 | padding: var(--space-05); 16 | } 17 | 18 | a { 19 | text-decoration: none; 20 | } 21 | 22 | a:hover, 23 | button:hover { 24 | cursor: pointer; 25 | transition: ease-in-out var(--duration-slow); 26 | } 27 | 28 | ul { 29 | list-style-type: none; 30 | } 31 | 32 | h1 { 33 | font-family: var(--title-font); 34 | font-size: var(--large-size); 35 | text-align: center; 36 | } 37 | 38 | h2 { 39 | font-family: var(--highlight-font); 40 | font-size: var(--medium-size); 41 | } 42 | 43 | h3 { 44 | font-family: var(--body-font); 45 | font-size: var(--medium-size); 46 | font-weight: var(--light-weight); 47 | } 48 | 49 | .page { 50 | min-height: 100vh; 51 | } 52 | -------------------------------------------------------------------------------- /src/components/Pagination/Pagination.tsx: -------------------------------------------------------------------------------- 1 | import Button from '../Button/Button'; 2 | import { PaginationProps } from './Pagination.types'; 3 | 4 | import './Pagination.css'; 5 | 6 | export const Pagination = ({ 7 | page, 8 | totalPages, 9 | handlePagination, 10 | }: PaginationProps) => { 11 | const scrollToTop = () => { 12 | window.scrollTo({ top: 0, behavior: 'smooth' }); 13 | }; 14 | 15 | return ( 16 |
17 |
18 | {page > 1 && ( 19 | 27 | )} 28 | {page < totalPages && ( 29 | 37 | )} 38 |
39 |
40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/pages/Zodiac/Zodiac.css: -------------------------------------------------------------------------------- 1 | .zodiac { 2 | min-height: 100vh; 3 | } 4 | 5 | .astrology__image { 6 | display: block; 7 | margin: auto; 8 | width: 15rem; 9 | margin-bottom: -5rem; 10 | margin-top: var(--space-20); 11 | border-radius: 0 0 50% 50%; 12 | } 13 | 14 | .astrology__list { 15 | display: grid; 16 | grid-template-columns: repeat(1, 1fr); 17 | padding: var(--space-20) 0; 18 | } 19 | 20 | @media (min-width: 768px) { 21 | .astrology__image { 22 | margin-bottom: 0; 23 | } 24 | 25 | .astrology__list { 26 | grid-template-columns: repeat(2, 1fr); 27 | padding: var(--space-30); 28 | } 29 | } 30 | 31 | @media (min-width: 992px) { 32 | .astrology__image { 33 | margin-bottom: -5.5rem; 34 | border-radius: 0; 35 | } 36 | 37 | .astrology__list { 38 | display: grid; 39 | grid-template-columns: repeat(3, 1fr); 40 | margin: auto; 41 | } 42 | } 43 | 44 | @media (min-width: 1400px) { 45 | .astrology__list { 46 | width: 80%; 47 | } 48 | } 49 | 50 | @media (min-width: 2360px) { 51 | .astrology__list { 52 | width: 70%; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/components/Accordion/Accordion.css: -------------------------------------------------------------------------------- 1 | .accordion__item { 2 | width: 90%; 3 | margin: auto; 4 | } 5 | 6 | .accordion__title { 7 | display: flex; 8 | flex-direction: row; 9 | align-items: center; 10 | justify-content: space-between; 11 | background-color: var(--quaternary-foreground); 12 | padding: var(--space-05); 13 | border: 2px solid var(--primary-foreground); 14 | } 15 | 16 | .accordion__title:hover { 17 | transition: ease-in-out var(--duration-standard); 18 | background-color: var(--primary-background); 19 | cursor: pointer; 20 | } 21 | 22 | .accordion__content { 23 | transition: ease-in-out var(--duration-quick); 24 | max-height: 0; 25 | overflow: hidden; 26 | text-align: justify; 27 | margin: 0.5rem; 28 | opacity: 0; 29 | } 30 | 31 | .accordion__content.dropped { 32 | transition: ease-in-out var(--duration-slow); 33 | max-height: 600px; 34 | margin: 1.5rem; 35 | opacity: 1; 36 | } 37 | 38 | @media (min-width: 768px) { 39 | .accordion__item { 40 | width: 60%; 41 | } 42 | } 43 | 44 | @media (min-width: 1200px) { 45 | .accordion__item { 46 | width: 50%; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/components/FlipCard/FlipCard.tsx: -------------------------------------------------------------------------------- 1 | import Button from '../Button/Button.tsx'; 2 | import { TarotCardProps } from '../../types/Tarot.types.ts'; 3 | 4 | import './FlipCard.css'; 5 | 6 | function TarotCard({ card }: TarotCardProps) { 7 | return ( 8 |
9 |
10 |
11 | {card.name} 12 |
13 |
14 |

{card.name}

15 |
16 |

Type: {card.type}

17 |

Upright: {card.upright.join(', ')}

18 |

Reversed: {card.reversed.join(', ')}

19 |
20 |
28 |
29 |
30 | ); 31 | } 32 | 33 | export default TarotCard; 34 | -------------------------------------------------------------------------------- /src/styles/global/color-variables.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --black: rgb(0, 0, 0); 3 | --black-sheer: rgba(0, 0, 0, 0.7); 4 | --dark-grey: rgb(83, 83, 83); 5 | --light-grey: rgb(206, 206, 206); 6 | --white: rgb(255, 255, 255); 7 | --white-sheer: rgba(255, 255, 255, 0.8); 8 | --orange: rgb(255, 193, 77); 9 | --purple: rgb(90, 0, 169); 10 | } 11 | 12 | html[data-theme='dark'] { 13 | --primary-foreground: var(--white-sheer); 14 | --secondary-foreground: var(--white); 15 | --tertiary-foreground: var(--white-sheer); 16 | --quaternary-foreground: var(--purple); 17 | --primary-background: var(--black); 18 | --secondary-background: var(--black-sheer); 19 | --tertiary-background: var(--black); 20 | --quaternary-background: var(--purple); 21 | } 22 | 23 | html[data-theme='light'] { 24 | --primary-foreground: var(--black); 25 | --secondary-foreground: var(--dark-grey); 26 | --tertiary-foreground: var(--white); 27 | --quaternary-foreground: var(--orange); 28 | --primary-background: var(--white); 29 | --secondary-background: var(--white-sheer); 30 | --tertiary-background: var(--black); 31 | --quaternary-background: var(--orange); 32 | } 33 | -------------------------------------------------------------------------------- /src/components/Button/Button.css: -------------------------------------------------------------------------------- 1 | .button--shadow { 2 | color: var(--primary-foreground); 3 | text-decoration: none; 4 | padding: 0.5rem; 5 | margin: 20px; 6 | border: 2px solid var(--primary-foreground); 7 | &:after { 8 | background-color: var(--primary-foreground); 9 | } 10 | &:hover { 11 | top: 5px; 12 | box-shadow: none; 13 | &:after { 14 | position: absolute; 15 | top: 0em; 16 | right: 0em; 17 | left: 0em; 18 | bottom: 0em; 19 | } 20 | } 21 | &:active { 22 | background-color: var(--quaternary-foreground); 23 | color: var(--primary-foreground); 24 | transition: color 200ms, background-color 200ms; 25 | } 26 | } 27 | 28 | .shadow { 29 | transition: all 100ms; 30 | background-color: var(--primary-background); 31 | position: relative; 32 | box-shadow: 4px 4px 4px 0px rgba(50, 50, 50, 0.05); 33 | &:after { 34 | transition: all 100ms; 35 | content: ''; 36 | display: block; 37 | z-index: -1; 38 | background-color: var(--primary-foreground); 39 | position: absolute; 40 | top: 0.5em; 41 | right: -0.4em; 42 | left: 0.4em; 43 | bottom: -0.4em; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/data/lettersData.ts: -------------------------------------------------------------------------------- 1 | // Credits to @themuuln on Github for the API data and formulas 2 | 3 | export const soulUrgeLetters: { [key: string]: number } = { 4 | А: 1, 5 | И: 1, 6 | О: 7, 7 | Ө: 8, 8 | У: 4, 9 | Ү: 5, 10 | Э: 6, 11 | A: 1, 12 | E: 5, 13 | I: 9, 14 | O: 6, 15 | U: 3, 16 | }; 17 | 18 | export const expressionLetters: { [key: string]: number } = { 19 | А: 1, 20 | Б: 2, 21 | В: 3, 22 | Г: 4, 23 | Д: 5, 24 | Е: 6, 25 | Ё: 7, 26 | Ж: 8, 27 | З: 9, 28 | И: 1, 29 | Й: 2, 30 | К: 3, 31 | Л: 4, 32 | М: 5, 33 | Н: 6, 34 | О: 7, 35 | Ө: 8, 36 | П: 9, 37 | Р: 1, 38 | С: 2, 39 | Т: 3, 40 | У: 4, 41 | Ү: 5, 42 | Ф: 6, 43 | Х: 7, 44 | Ц: 8, 45 | Ч: 9, 46 | Ш: 1, 47 | Щ: 2, 48 | Ъ: 3, 49 | Ь: 4, 50 | Ы: 5, 51 | Э: 6, 52 | Ю: 7, 53 | Я: 8, 54 | // latin 55 | A: 1, 56 | B: 2, 57 | C: 3, 58 | D: 4, 59 | E: 5, 60 | F: 6, 61 | G: 7, 62 | H: 8, 63 | I: 9, 64 | J: 1, 65 | K: 2, 66 | L: 3, 67 | M: 4, 68 | N: 5, 69 | O: 6, 70 | P: 7, 71 | Q: 8, 72 | R: 9, 73 | S: 1, 74 | T: 2, 75 | U: 3, 76 | V: 4, 77 | W: 5, 78 | X: 6, 79 | Y: 7, 80 | Z: 9, 81 | }; 82 | -------------------------------------------------------------------------------- /src/pages/About/About.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import shapes from '../../assets/images/shapes.gif'; 3 | import Accordion from '../../components/Accordion/Accordion'; 4 | import { accordionData } from '../../data/accordionData'; 5 | import './About.css'; 6 | 7 | export default function About() { 8 | const [activeIndex, setActiveIndex] = useState(-1); 9 | 10 | const toggleAccordion = (index: number) => { 11 | setActiveIndex(index === activeIndex ? -1 : index); 12 | }; 13 | 14 | return ( 15 |
16 |

About

17 |

So what is Lunar, really?

18 |

19 | In this section I will walk you through all you need to know about my 20 | website. 21 |

22 | Geometric shapes 23 |
24 | {accordionData.map(({ title, content }, index) => ( 25 | toggleAccordion(index)} 31 | /> 32 | ))} 33 |
34 |
35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /src/components/ImageCarousel/ImageCarousel.css: -------------------------------------------------------------------------------- 1 | .carousel { 2 | display: flex; 3 | align-items: center; 4 | justify-content: center; 5 | flex-direction: column; 6 | margin-top: var(--space-30); 7 | } 8 | 9 | .carousel span { 10 | display: block; 11 | text-align: center; 12 | font-weight: var(--bold-weight); 13 | padding: var(--space-10); 14 | } 15 | 16 | .results { 17 | font-size: var(--small-size); 18 | } 19 | 20 | .box__buttons { 21 | display: flex; 22 | justify-content: flex-start; 23 | margin: 1rem; 24 | } 25 | 26 | .box__buttons button { 27 | margin: var(--space-03); 28 | font-size: var(--small-size); 29 | border: 2px solid var(--primary-foreground); 30 | color: var(--primary-foreground); 31 | background-color: var(--primary-background); 32 | } 33 | 34 | .box__buttons button:hover { 35 | background: var(--primary-background); 36 | background: radial-gradient( 37 | circle, 38 | var(--quaternary-background) 0%, 39 | var(--primary-background) 70% 40 | ); 41 | } 42 | 43 | .box__ul { 44 | width: 20rem; 45 | display: flex; 46 | justify-items: center; 47 | flex-direction: row; 48 | overflow-x: hidden; 49 | gap: 1rem; 50 | } 51 | 52 | .box__ul li img { 53 | width: 20rem; 54 | height: 20rem; 55 | } 56 | 57 | .box-ul li span { 58 | text-align: center; 59 | } 60 | -------------------------------------------------------------------------------- /src/components/ThemeToggle/ThemeToggle.css: -------------------------------------------------------------------------------- 1 | .toggle__switch { 2 | position: relative; 3 | display: inline-block; 4 | width: 40px; 5 | height: 20px; 6 | margin-top: 3px; 7 | } 8 | 9 | .toggle__switch input { 10 | opacity: 0; 11 | width: 0; 12 | height: 0; 13 | } 14 | 15 | .toggle__slider { 16 | position: absolute; 17 | cursor: pointer; 18 | top: 0; 19 | left: 0; 20 | right: 0; 21 | bottom: 0; 22 | background-color: var(--light-grey); 23 | transition: 0.4s; 24 | border-radius: 20px; 25 | } 26 | 27 | .toggle__slider:before { 28 | position: absolute; 29 | content: ''; 30 | height: 16px; 31 | width: 16px; 32 | left: 2px; 33 | bottom: 2px; 34 | background-color: var(--black); 35 | transition: 0.4s; 36 | border-radius: 50%; 37 | } 38 | 39 | input:checked + .toggle__slider { 40 | background-color: var(--dark-grey); 41 | } 42 | 43 | input:checked + .toggle__slider:before { 44 | transform: translateX(20px); 45 | } 46 | 47 | .toggle__slider.round { 48 | border-radius: 20px; 49 | } 50 | 51 | .toggle__slider.round:before { 52 | border-radius: 50%; 53 | } 54 | 55 | @media (max-width: 767px) { 56 | .toggle__switch { 57 | right: 23px; 58 | } 59 | } 60 | 61 | @media (min-width: 1400px) { 62 | .toggle__switch { 63 | top: 2px; 64 | } 65 | } 66 | 67 | @media (min-width: 2360px) { 68 | .toggle__switch { 69 | top: 10px; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.css: -------------------------------------------------------------------------------- 1 | footer { 2 | position: relative; 3 | bottom: 0; 4 | left: 0; 5 | right: 0; 6 | padding-top: var(--space-20); 7 | border-top: 2px solid var(--light-grey); 8 | } 9 | 10 | .footer__contact { 11 | display: flex; 12 | justify-content: center; 13 | text-align: start; 14 | width: 90%; 15 | margin: auto; 16 | font-size: var(--small-size); 17 | font-weight: var(--regular-weight); 18 | } 19 | 20 | .footer__download, 21 | .footer__socials { 22 | display: flex; 23 | flex-direction: row; 24 | justify-content: center; 25 | align-items: center; 26 | gap: var(--space-10); 27 | font-size: var(--space-15); 28 | padding: var(--space-10); 29 | } 30 | 31 | .footer__download img:hover { 32 | cursor: pointer; 33 | } 34 | 35 | .footer__socials li a { 36 | color: var(--primary-foreground); 37 | } 38 | 39 | .footer__socials li a i:hover { 40 | color: var(--secondary-foreground); 41 | transition: ease-in-out var(--duration-standard); 42 | } 43 | 44 | .footer__copyright { 45 | background-color: var(--tertiary-background); 46 | color: var(--tertiary-foreground); 47 | font-size: var(--small-size); 48 | text-align: center; 49 | padding: var(--space-05); 50 | } 51 | 52 | @media (min-width: 768px) { 53 | .footer__contact { 54 | width: 40%; 55 | } 56 | } 57 | 58 | @media (min-width: 992px) { 59 | .footer__contact { 60 | width: 100%; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/components/MagicCard/MagicCard.tsx: -------------------------------------------------------------------------------- 1 | import Button from '../Button/Button.tsx'; 2 | import { TarotCardProps } from '../../types/Tarot.types.ts'; 3 | 4 | import './MagicCard.css'; 5 | 6 | function getRandomBoolean() { 7 | return Math.random() < 0.5; 8 | } 9 | 10 | function TarotCard({ card }: TarotCardProps) { 11 | const uprightReversed = getRandomBoolean(); 12 | 13 | return ( 14 |
15 |
16 |
17 | ? 18 |
19 |
20 |

{card.name}

21 |
22 |

23 | This card is 24 | {uprightReversed ? ' reversed' : ' upright'}. 25 |

26 |

27 | Reflect on the following keywords: {''} 28 | {uprightReversed 29 | ? card.reversed.join(', ') 30 | : card.upright.join(', ')} 31 |

32 |
33 |
41 |
42 |
43 | ); 44 | } 45 | 46 | export default TarotCard; 47 | -------------------------------------------------------------------------------- /src/components/Footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | import downloadApple from '../../assets/images/download-apple.svg'; 2 | import downloadAndroid from '../../assets/images/download-android.svg'; 3 | 4 | import './Footer.css'; 5 | 6 | export default function Footer() { 7 | return ( 8 |
9 |
10 | Download on the App Store 11 | Download on Google Play 12 |
13 |

14 | Got any feedback? Up for a chat? Shoot me an email or direct message the 15 | socials below. 16 |

17 | 39 |
40 |

© 2023 Lunar. All Rights Reserved.

41 |
42 |
43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /src/pages/Zodiac/Zodiac.tsx: -------------------------------------------------------------------------------- 1 | import owlHead from '../../assets/images/owl-head.webp'; 2 | import useFetch from '../../hooks/useFetch'; 3 | import ZodiacCard from '../../components/ZodiacCard/ZodiacCard.tsx'; 4 | import Loading from '../../components/Loading/Loading.tsx'; 5 | import { ZodiacProps } from '../../types/Zodiac.types.ts'; 6 | 7 | import './Zodiac.css'; 8 | 9 | function Zodiac() { 10 | const url = 'https://jps-tarot-api.azurewebsites.net/api/Zodiac/Get'; 11 | const { data: signs, error, loading } = useFetch(url); 12 | 13 | if (error) { 14 | console.log(`Error: ${error.message}`); 15 | } 16 | 17 | return ( 18 |
19 |

Astrology

20 | {error &&
{error?.message}
} 21 | {loading && } 22 |

23 | Astrology is an ancient and complex system of divination that has been 24 | practiced for thousands of years. Its origins can be traced back to 25 | various civilizations throughout history, including Mesopotamia, Egypt, 26 | and Greece. The Western zodiac, also known as the tropical zodiac, is 27 | based on the position of the Sun relative to the twelve zodiac signs at 28 | the time of a person's birth. Each sign is associated with specific 29 | personality traits and characteristics. 30 |

31 | Owl headed man 32 |
33 | {signs?.map((sign) => ( 34 | 35 | ))} 36 |
37 |
38 | ); 39 | } 40 | 41 | export default Zodiac; 42 | -------------------------------------------------------------------------------- /src/pages/Tarot/Tarot.css: -------------------------------------------------------------------------------- 1 | .tarot { 2 | min-height: 100vh; 3 | } 4 | 5 | .card__list { 6 | display: grid; 7 | grid-template-columns: repeat(1, 1fr); 8 | padding: 2rem; 9 | gap: 1rem; 10 | } 11 | 12 | .filter-buttons { 13 | display: flex; 14 | flex-direction: column; 15 | align-items: center; 16 | justify-content: center; 17 | margin-bottom: var(--space-10); 18 | } 19 | 20 | .filter-input { 21 | width: 90%; 22 | margin: auto; 23 | display: flex; 24 | flex-direction: column; 25 | align-items: center; 26 | justify-content: center; 27 | padding: var(--space-10); 28 | } 29 | 30 | .search-input { 31 | display: block; 32 | margin: 1rem auto; 33 | padding: 0.5rem; 34 | border: 2px solid var(--primary-foreground); 35 | background-color: var(--primary-background); 36 | color: var(--primary-foreground); 37 | } 38 | 39 | /* .card__item { 40 | border: 1px solid white; 41 | background-color: rgb(23, 23, 23); 42 | width: 90%; 43 | } */ 44 | 45 | @media (min-width: 640px) { 46 | .filter-buttons { 47 | flex-direction: row; 48 | } 49 | 50 | .card__list { 51 | display: grid; 52 | grid-template-columns: repeat(2, 1fr); 53 | margin: auto; 54 | width: 90%; 55 | } 56 | } 57 | 58 | @media (min-width: 920px) { 59 | .card__list { 60 | display: flex; 61 | flex-wrap: wrap; 62 | justify-content: center; 63 | margin: auto; 64 | } 65 | } 66 | 67 | @media (min-width: 1300px) { 68 | .card__list { 69 | display: grid; 70 | grid-template-columns: repeat(4, 1fr); 71 | width: 70%; 72 | } 73 | } 74 | 75 | @media (min-width: 2360px) { 76 | .card__list { 77 | display: grid; 78 | grid-template-columns: repeat(6, 1fr); 79 | width: 50%; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Route, Routes } from 'react-router-dom'; 2 | import { createContext, useState } from 'react'; 3 | 4 | import Header from './components/Header/Header'; 5 | import Footer from './components/Footer/Footer'; 6 | 7 | import Home from '../src/pages/Home/Home'; 8 | import About from './pages/About/About'; 9 | import Tarot from './pages/Tarot/Tarot'; 10 | import Zodiac from './pages/Zodiac/Zodiac'; 11 | import ZodiacSignDetails from './pages/ZodiacSignDetails/ZodiacSignDetails'; 12 | import NotFound from './pages/NotFound/NotFound'; 13 | import Numerology from './pages/Numerology/Numerology'; 14 | 15 | import './App.css'; 16 | 17 | type Theme = 'light' | 'dark'; 18 | type ThemeContext = { theme: Theme; toggleTheme: () => void }; 19 | 20 | export const ThemeContext = createContext({} as ThemeContext); 21 | 22 | function App() { 23 | const [theme, setTheme] = useState('light'); 24 | 25 | const toggleTheme = () => { 26 | setTheme(theme === 'light' ? 'dark' : 'light'); 27 | }; 28 | 29 | document.documentElement.setAttribute('data-theme', theme); 30 | 31 | return ( 32 | 33 |
34 | 35 | } /> 36 | } /> 37 | } /> 38 | } /> 39 | } /> 40 | } /> 41 | } /> 42 | 43 |