Commit 16cc6dcc by Syed Abdul Rahman

tailwind css conversion in progress

parent 07289bf0
import type { Preview } from '@storybook/nextjs'
import { useEffect } from 'react'
import '../src/app/fonts.css'
import '../src/app/globals.css'
const ThemeDecorator = (Story: any, context: any) => {
const backgroundValue = context.globals.backgrounds?.value
const theme = backgroundValue == 'dark' ? 'dark-theme' : ''
......
......@@ -11,7 +11,9 @@
"build-storybook": "storybook build"
},
"dependencies": {
"@tailwindcss/line-clamp": "^0.4.4",
"axios": "^1.10.0",
"classnames": "^2.5.1",
"next": "15.3.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
......@@ -20,13 +22,16 @@
"devDependencies": {
"@eslint/eslintrc": "^3",
"@storybook/nextjs": "^9.0.12",
"@tailwindcss/postcss": "^4.1.11",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "15.3.4",
"eslint-plugin-storybook": "^9.0.12",
"postcss": "^8.5.6",
"storybook": "^9.0.12",
"tailwindcss": "^4.1.11",
"typescript": "^5"
}
}
/** @type {import('tailwindcss').Config} */
export default {
plugins: {
"@tailwindcss/postcss": {},
},
};
@import "tailwindcss";
:root {
--primary-bg-color: #ffffff;
--secondary-bg-color: #f8f9fa;
......
......@@ -3,7 +3,6 @@ import CardWrapper from "@/components/TopLevel/CardList/CardWrapper/CardWrapper"
import Pagination from "@/components/Shared/Pagination/Pagination";
import { useEffect, useState } from "react";
import { getBlogPosts } from "@/lib/Api";
export default function Home() {
const [currentPage, setCurrentPage] = useState(1);
const [posts, setPosts] = useState<any>();
......
......@@ -59,15 +59,13 @@
background-color: rgb(115, 101, 243);
}
.loader {
width: 20px;
height: 20px;
/* .loader {
border-radius: 50%;
border: 1px solid gray;
border-top: 2px solid blue;
display: inline-block;
animation: rotate 0.5s linear infinite;
}
} */
.loader-sm {
width: 10px;
......@@ -94,11 +92,11 @@
font-family: "Inter-Medium";
}
@keyframes rotate {
/* @keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
} */
......@@ -12,7 +12,7 @@ export const Primary = {
<>
<h2 className={styles.storyTitle}>Size</h2>
<section className={styles["btn-wrapper"]}>
<Button variant="primary" size="sm">
<Button variant="primary" size="sm" disabled={false}>
Small
</Button>
<Button variant="primary" size="md">
......@@ -58,15 +58,6 @@ export const Primary = {
Button
</Button>
</section>
<h2 className={styles.storyTitle}>Pagination Next & Prev</h2>
<section className={styles["btn-wrapper"]}>
<Button variant="primary" size="md">
{"<<Prev"}
</Button>
<Button variant="primary" size="md">
{"Next>>"}
</Button>
</section>
</>
);
},
......
import styles from "./Button.module.css";
import classNames from "classnames";
type ButtonProps = {
variant: "primary" | "secondary";
size: "sm" | "md" | "lg";
active?: boolean;
loading?: boolean;
disabled?: boolean;
children: React.ReactNode;
} & React.ButtonHTMLAttributes<HTMLButtonElement>;
const Button = ({
variant,
size,
......@@ -7,19 +17,38 @@ const Button = ({
disabled,
children,
...buttonProps
}: any) => {
}: ButtonProps) => {
const buttonClass = classNames(
"flex items-center justify-center gap-2 font-bold rounded-xl cursor-pointer box-border",
{
"px-4 py-2 text-sm": size === "sm",
"px-5 py-2.5 text-base": size === "md",
"px-8 py-4 text-lg": size === "lg",
"bg-[var(--primary-bg-color)] text-[var(--primary-text-color)] hover:bg-indigo-500 hover:text-white":
variant === "primary" && !disabled,
"bg-purple-700 text-white border border-purple-700 hover:bg-white hover:text-gray-600":
variant === "secondary" && !disabled,
"bg-indigo-500 text-white": active,
"bg-[var(--disabled-button-bg)] text-gray-400 pointer-events-none":
disabled,
}
);
const loaderClass = classNames(
"border border-gray-300 border-t-2 border-t-blue-600 rounded-full animate-spin",
{
"w-2.5 h-2.5": size === "sm",
"w-3.5 h-3.5": size === "md",
"w-5 h-5": size === "lg",
}
);
return (
<button
className={`${styles["button"]} ${styles[variant]} ${styles[size]} ${
active ? styles["button-active"] : ""
} ${disabled ? styles["disabled"] : ""}`}
{...buttonProps}
>
<button className={buttonClass} {...buttonProps}>
{loading && (
<>
<div
className={`${styles[`loader-${size}`]} ${styles["loader"]}`}
></div>
<div className={loaderClass}></div>
<span>Loading</span>
</>
)}
......
......@@ -6,10 +6,7 @@
}
.inputWrapper input {
border: none;
outline: none;
outline: none;
border: none;
height: 12px;
width: 100%;
font-size: 15px;
......@@ -24,6 +21,10 @@
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
}
.input-style {
border-top-right-radius: 10px !important;
border-bottom-right-radius: 10px;
}
.input::placeholder {
color: var(--secondary-text-color);
......@@ -33,10 +34,10 @@
font-family: "Inter-Medium";
padding: 0.5rem 0;
}
.svgWrapper {
height: 12px;
padding: 1.1rem 1rem;
/* background-color: #cccccc43; */
background-color: var(--secondary-bg-color);
border-top-left-radius: 10px;
border-bottom-left-radius: 10px;
......@@ -45,6 +46,7 @@
.disabled {
display: none;
}
.error {
box-shadow: 0px 0px 3px 3px rgb(223, 56, 56);
}
......@@ -56,7 +58,3 @@
padding: 0.75rem;
cursor: pointer;
}
.input-style {
border-top-right-radius: 10px !important;
border-bottom-right-radius: 10px;
}
"use client";
import Image from "next/image";
import styles from "./Input.module.css";
import img from "../../../../public/imgaes/clear-icon.svg";
import classNames from "classnames";
type InputProps = {
label?: string;
......@@ -20,18 +19,45 @@ export default function Input({
}: InputProps) {
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Enter") {
console.log("Entered value:", value);
onEnter?.(value as string);
}
};
const inputWrapper = classNames(
"flex items-center rounded-[10px] border-[rgba(255,255,255,0.451)] border-[0.2px]",
{ "shadow-[0_0_3px_3px_rgb(223,56,56)]": error === true }
);
const inputClass = classNames(
"outline-none h-[12px] w-full text-[15px] text-[var(--primary-text-color)] bg-[var(--secondary-bg-color)] py-[1.5rem] font-[Inter-Medium]",
{
"!px-[1rem] !py-[1.5rem] rounded-tl-[10px] rounded-bl-[10px]":
value != "",
"!rounded-tr-[10px] rounded-br-[10px]": value == "",
}
);
const searchWrapper = classNames(
"py-[1rem] px-[1rem] bg-[var(--secondary-bg-color)] rounded-tl-[10px] rounded-bl-[10px]",
{
hidden: value != "",
}
);
const clearWrapper = classNames(
"rounded-tr-[10px] rounded-br-[10px] bg-[var(--secondary-bg-color)] p-[1rem] cursor-pointer",
{
hidden: value === "",
}
);
return (
<>
{label && <div className={styles.label}>{label}</div>}
<div className={`${styles.inputWrapper} ${error ? styles.error : ""}`}>
<div
className={`${styles.svgWrapper}
${value != "" ? styles.disabled : ""}`}
>
{label && (
<div className={"font-[Inter-Medium] py-[0.5rem]"}>{label}</div>
)}
<div className={inputWrapper}>
<div className={searchWrapper}>
<svg
width="20px"
height="15px"
......@@ -48,7 +74,6 @@ export default function Input({
strokeWidth="0.384"
></g>
<g id="SVGRepo_iconCarrier">
{" "}
<path
d="M15.7955 15.8111L21 21M18 10.5C18 14.6421 14.6421 18 10.5 18C6.35786 18 3 14.6421 3 10.5C3 6.35786 6.35786 3 10.5 3C14.6421 3 18 6.35786 18 10.5Z"
stroke="#787373"
......@@ -60,20 +85,13 @@ export default function Input({
</svg>
</div>
<input
className={`${styles.input} ${
value != "" ? styles["input-spl"] : styles["input-style"]
}`}
className={inputClass}
value={value}
onKeyDown={handleKeyDown}
name="input"
{...props}
/>
<div
className={`${styles["clear-wrapper"]} ${
value == "" ? styles.disabled : ""
} `}
onClick={onClear}
>
<div className={clearWrapper} onClick={onClear}>
<Image height={20} src={img} alt="img" />
</div>
</div>
......
......@@ -2,9 +2,8 @@
import Input from "@/components/Base/Input/Input";
import themeLogo from "../../../../public/imgaes/moon-svgrepo-com.svg";
import lightTheme from "../../../../public/imgaes/sun.svg";
import styles from "./Header.module.css";
import Image from "next/image";
import { useEffect, useState } from "react";
import { useState } from "react";
export default function Header() {
const [isDark, setIsDark] = useState("");
const [searchValue, setSearchValue] = useState("");
......@@ -29,9 +28,19 @@ export default function Header() {
};
return (
<header className={styles["header-wrapper"]}>
<div className={styles.headerTitle}>NewsBlog</div>
<div className={styles["input-container"]}>
<header
className={
"bg-[var(--primary-bg-color)] font-[Inter-Bold] flex justify-around items-center shadow-[0_4px_12px_rgba(0,0,0,0.08)] py-4 w-full sticky top-0"
}
>
<div
className={
"text-[var(--primary-text-color)] font-[Inter-Bold] text-[22px]"
}
>
NewsBlog
</div>
<div className={"w-[30%]"}>
<Input
placeholder={"Discover news, articles and more..."}
value={searchValue}
......@@ -40,7 +49,7 @@ export default function Header() {
onEnter={handleSearchEnter}
/>
</div>
<div className={styles["theme-switcher"]} onClick={handleThemeToggle}>
<div className="cursor-pointer" onClick={handleThemeToggle}>
{isDark == "dark-theme" ? (
<Image src={lightTheme} alt="image" width={25} />
) : (
......
......@@ -8,39 +8,3 @@
background-color: var(--secondary-bg-color);
padding: 2rem;
}
.button {
padding: 6px 12px;
border: 1px solid #ccc;
border-radius: 4px;
color: #333;
text-decoration: none;
font-size: 14px;
font-family: "Inter-Bold";
transition: background-color 0.2s ease;
background: white;
}
.button:hover {
background-color: #f0f0f0;
}
.active {
background-color: #0070f3;
color: white;
border-color: #0070f3;
pointer-events: none;
}
.dots {
padding: 6px 12px;
border: none;
background: none;
font-size: 16px;
cursor: pointer;
color: #666;
}
.dots:hover {
color: #000;
}
......@@ -51,9 +51,12 @@ export default function Pagination({
}
});
};
return (
<div className={styles.wrapper}>
<div
className={
"flex justify-center align-senter gap-[8px] text-[Inter-Medium] bg-[var(--secondary-bg-color)] p-[2rem] flex-wrap"
}
>
{currentPage > 1 && (
<Button
variant="primary"
......
......@@ -26,7 +26,7 @@ article {
border-radius: 15px;
object-fit: stretch;
width: 100%;
object-fit: cover; /* or 'fill' / 'contain' based on need */
object-fit: cover;
display: block;
}
......
.customLink {
text-decoration: none;
color: inherit;
}
.card {
width: 350px;
border-radius: 10px;
......@@ -17,9 +22,6 @@
font-family: "Inter-Medium";
}
.card figure {
border: 1px solid black;
}
.card-title {
color: var(--primary-text-color);
display: -webkit-box;
......@@ -51,6 +53,9 @@
gap: 1rem;
padding-bottom: 1rem;
}
.author-avatar {
border-radius: 50%;
}
.author-name {
color: rgb(95, 89, 89);
......@@ -75,12 +80,3 @@
align-items: center;
gap: 3px;
}
.author-avatar {
border-radius: 50%;
}
.customLink {
text-decoration: none;
color: inherit;
}
......@@ -9,9 +9,9 @@ export default {
export const Default = {
args: {
cardTitle:
title:
"Lorem ipsum dolor adipiscing amet Lorem ipsum dolor adipiscing amet ",
cardDescription: "Lorem ipsum dolor adipiscing amet, consectetur sit .",
description: "Lorem ipsum dolor adipiscing amet, consectetur sit .",
authorName: "Abdul Rahman",
postedDate: "July 10, 2025",
readTime: "2",
......
import styles from "./Card.module.css";
import clock from "../../../../../public/imgaes/clock.svg";
import Image from "next/image";
import Link from "next/link";
function Card({
imgUrl,
cardTitle,
cardDescription,
title,
description,
authorName,
postedDate,
readTime,
}: any) {
return (
<Link href={"/"} className={styles.customLink}>
<article className={styles.card}>
<Link href={"/"} className="no-underline text-inherit">
<article
className={
"!w-[350px] rounded-[10px] bg-[var(--primary-bg-color)] shadow-[0px_4px_12px_rgba(0,0,0,0.1)]"
}
>
<img
className={styles["card-avatar"]}
className="w-full h-[200px] object-cover rounded-t-[10px]"
src={imgUrl}
alt="Author photo"
/>
<section className={styles["card-content"]}>
<h3 className={styles["card-title"]}>{cardTitle}</h3>
<p className={styles["card-description"]}>{cardDescription}</p>
<section className="px-4 ">
<h3 className="text-[var(--primary-text-color)] font-[Inter-Bold] line-clamp-2 text-[20px] mt-8">
{title}
</h3>
<p className="text-[var(--secondary-text-color)] font-[Inter-Medium] text-[16px] line-clamp-2 mt-3">
{description}
</p>
</section>
<footer className={styles["card-footer"]}>
<div className={styles["author-details"]}>
<footer className="px-4 mt-3 pb-3">
<div className="flex items-center gap-4 pb-4">
<div>
<img
src="https://picsum.photos/55/55"
alt="Author photo"
className={styles["author-avatar"]}
className="rounded-full"
/>
</div>
<div className="author-info">
<div className={styles["author-name"]}>{authorName}</div>
<div className={styles["post-info"]}>
<div className="text-[var(--primary-text-color)] font-[Inter-Bold]">
{authorName}
</div>
<div className="flex items-center gap-4 font-[Inter-Regular] text-[var(--secondary-text-color)] text-[14px] py-[2px]">
<div>{postedDate}.</div>
<div className={styles.readtimeWrapper}>
<div className="flex items-center gap-[3px]">
<Image width={20} src={clock} alt="img" />
{readTime} min
</div>
......
"use client";
import { Fragment } from "react";
import Card from "../Card/Card";
import styles from "./CardWrapper.module.css";
......@@ -78,12 +77,12 @@ const data = [
function CardWrapper({ cardData }: any) {
return (
<div className={styles.wrapperContainer}>
<div className="grid grid-cols-[repeat(3,350px)] gap-x-8 gap-y-8 justify-center box-border bg-[var(--secondary-bg-color)] py-8">
{cardData?.map((ele: any) => (
<Fragment key={ele.id}>
<Card
cardTitle={ele.Title}
cardDescription={ele.Description}
title={ele.Title}
description={ele?.Description}
authorName={ele.Author}
imgUrl={`http://localhost:1337${ele.Image.url}`}
postedDate={ele.PostedOn}
......
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx}', // If using Pages Router
'./app/**/*.{js,ts,jsx,tsx}', // If using App Router
'./components/**/*.{js,ts,jsx,tsx}'
],
theme: {
extend: {},
},
plugins: [require('@tailwindcss/line-clamp')],
}
......@@ -22,6 +22,6 @@
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "postcss.config.mjs"],
"exclude": ["node_modules"]
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment