_article-showcase.scss 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  1. // ==========================================================================
  2. // ARTICLE SHOWCASE - Article Display Component
  3. // ==========================================================================
  4. @use '../abstracts/variables' as v;
  5. @use '../abstracts/mixins' as mix;
  6. @use 'sass:map';
  7. @use 'sass:color';
  8. // ==========================================================================
  9. // Main Article Layout
  10. // ==========================================================================
  11. .article-showcase {
  12. padding: map.get(v.$spacers, 5) 0;
  13. background-color: var(--color-surface-scase);
  14. }
  15. .article-layout {
  16. @include mix.grid(1, map.get(v.$spacers, 4));
  17. @include mix.respond-to-min('lg') {
  18. @include mix.grid(3, map.get(v.$spacers, 5));
  19. -ms-grid-columns: 2fr 1fr;
  20. grid-template-columns: 2fr 1fr;
  21. }
  22. }
  23. // ==========================================================================
  24. // Main Article Content
  25. // ==========================================================================
  26. .article-main {
  27. background-color: var(--color-surface);
  28. padding: map.get(v.$spacers, 5);
  29. @include mix.border-radius(v.$border-radius-lg);
  30. @include mix.box-shadow(var(--box-shadow));
  31. }
  32. // Article Header
  33. .article-header {
  34. border-bottom: 1px solid v.$light-gray;
  35. padding-bottom: map.get(v.$spacers, 4);
  36. }
  37. .article-meta {
  38. @include mix.flex(row, flex-start, center, wrap);
  39. gap: map.get(v.$spacers, 3);
  40. @include mix.respond-to-max('sm') {
  41. @include mix.flex(column, flex-start, flex-start);
  42. gap: map.get(v.$spacers, 2);
  43. }
  44. }
  45. .article-category {
  46. font-size: map.get(v.$font-sizes, 'sm');
  47. font-weight: map.get(v.$font-weights, 'medium');
  48. @include mix.border-radius(v.$border-radius);
  49. text-transform: uppercase;
  50. letter-spacing: 0.5px;
  51. @include mix.transition(v.$transition-base);
  52. &:hover {
  53. background-color: color.scale(v.$primary, $lightness: -10%) !important;
  54. }
  55. }
  56. .article-date,
  57. .article-read-time {
  58. font-size: map.get(v.$font-sizes, 'sm');
  59. font-weight: map.get(v.$font-weights, 'regular');
  60. }
  61. .article-title {
  62. line-height: 1.2;
  63. margin-bottom: map.get(v.$spacers, 4);
  64. @include mix.respond-to-min('md') {
  65. font-size: map.get(v.$font-sizes, '4xl');
  66. }
  67. @include mix.respond-to-min('lg') {
  68. font-size: map.get(v.$font-sizes, '5xl');
  69. }
  70. }
  71. .article-author {
  72. @include mix.flex(row, flex-start, center);
  73. gap: map.get(v.$spacers, 3);
  74. margin-top: map.get(v.$spacers, 4);
  75. }
  76. .author-avatar {
  77. width: 60px;
  78. height: 60px;
  79. @include mix.border-radius(50%);
  80. @include mix.transition(v.$transition-base);
  81. overflow: hidden;
  82. &:hover {
  83. @include mix.transform(scale(1.05));
  84. }
  85. .image {
  86. width: 100%;
  87. object-fit: cover;
  88. }
  89. }
  90. .author-name {
  91. font-weight: map.get(v.$font-weights, 'semibold');
  92. font-size: map.get(v.$font-sizes, 'base');
  93. color: var(--color-text-muted);
  94. margin-bottom: 0.25rem;
  95. }
  96. .author-title {
  97. font-size: map.get(v.$font-sizes, 'sm');
  98. color: v.$gray;
  99. margin-bottom: 0;
  100. }
  101. // Featured Image
  102. .article-featured-image {
  103. margin: map.get(v.$spacers, 5) 0;
  104. img {
  105. width: 100%;
  106. height: auto;
  107. max-height: 400px;
  108. object-fit: cover;
  109. @include mix.border-radius(v.$border-radius-lg);
  110. @include mix.transition(v.$transition-base);
  111. &:hover {
  112. @include mix.transform(scale(1.02));
  113. }
  114. }
  115. }
  116. .image-caption {
  117. font-size: map.get(v.$font-sizes, 'sm');
  118. font-style: italic;
  119. color: v.$gray;
  120. margin-top: map.get(v.$spacers, 2);
  121. }
  122. // ==========================================================================
  123. // Article Content
  124. // ==========================================================================
  125. .article-content {
  126. font-family: v.$font-family-sans;
  127. .article-intro {
  128. font-size: map.get(v.$font-sizes, 'lg');
  129. font-weight: map.get(v.$font-weights, 'medium');
  130. color: v.$dark-gray;
  131. margin-bottom: map.get(v.$spacers, 4);
  132. line-height: 1.7;
  133. }
  134. h2 {
  135. font-family: v.$font-family-heading;
  136. font-weight: map.get(v.$font-weights, 'bold');
  137. font-size: map.get(v.$font-sizes, '2xl');
  138. margin: map.get(v.$spacers, 5) 0 map.get(v.$spacers, 3) 0;
  139. &:first-child {
  140. margin-top: 0;
  141. }
  142. }
  143. h3 {
  144. font-family: v.$font-family-heading;
  145. font-weight: map.get(v.$font-weights, 'semibold');
  146. font-size: map.get(v.$font-sizes, 'xl');
  147. margin: map.get(v.$spacers, 4) 0 map.get(v.$spacers, 2) 0;
  148. }
  149. p {
  150. font-size: map.get(v.$font-sizes, 'base');
  151. line-height: 1.7;
  152. margin-bottom: map.get(v.$spacers, 3);
  153. }
  154. .article-list {
  155. margin: map.get(v.$spacers, 3) 0;
  156. padding-left: map.get(v.$spacers, 4);
  157. li {
  158. margin-bottom: map.get(v.$spacers, 2);
  159. font-size: map.get(v.$font-sizes, 'base');
  160. line-height: 1.6;
  161. &::marker {
  162. color: v.$primary;
  163. }
  164. }
  165. }
  166. }
  167. .article-quote {
  168. background-color: color.scale(v.$primary, $lightness: 95%);
  169. border-left: 4px solid v.$primary;
  170. padding: map.get(v.$spacers, 4);
  171. margin: map.get(v.$spacers, 5) 0;
  172. font-size: map.get(v.$font-sizes, 'lg');
  173. font-style: italic;
  174. font-weight: map.get(v.$font-weights, 'medium');
  175. color: v.$dark-gray;
  176. @include mix.border-radius(v.$border-radius);
  177. @include mix.transition(v.$transition-base);
  178. &:hover {
  179. background-color: color.scale(v.$primary, $lightness: 90%);
  180. border-left-color: color.scale(v.$primary, $lightness: -10%);
  181. }
  182. }
  183. // ==========================================================================
  184. // Article Tags
  185. // ==========================================================================
  186. .article-tags {
  187. border-top: 1px solid v.$light-gray;
  188. padding-top: map.get(v.$spacers, 4);
  189. .tags-title {
  190. font-size: map.get(v.$font-sizes, 'base');
  191. font-weight: map.get(v.$font-weights, 'semibold');
  192. margin-bottom: map.get(v.$spacers, 3);
  193. }
  194. .tags-list {
  195. @include mix.flex(row, flex-start, center, wrap);
  196. gap: map.get(v.$spacers, 2);
  197. }
  198. .tag {
  199. background-color: v.$light-gray;
  200. color: v.$dark-gray;
  201. padding: 0.5rem 1rem;
  202. @include mix.border-radius(v.$border-radius);
  203. font-size: map.get(v.$font-sizes, 'sm');
  204. font-weight: map.get(v.$font-weights, 'medium');
  205. @include mix.transition(v.$transition-base);
  206. cursor: pointer;
  207. &:hover {
  208. background-color: v.$primary;
  209. color: v.$white;
  210. @include mix.transform(translateY(-2px));
  211. @include mix.box-shadow(v.$box-shadow);
  212. }
  213. &:active {
  214. @include mix.transform(translateY(0));
  215. }
  216. }
  217. }
  218. // ==========================================================================
  219. // Sidebar
  220. // ==========================================================================
  221. .article-sidebar {
  222. @include mix.flex(column, flex-start, stretch);
  223. gap: map.get(v.$spacers, 4);
  224. }
  225. .sidebar-widget {
  226. @include mix.transition(v.$transition-base);
  227. &:hover {
  228. @include mix.transform(translateY(-2px));
  229. @include mix.box-shadow(v.$box-shadow-lg);
  230. }
  231. .widget-title {
  232. font-family: v.$font-family-heading;
  233. font-weight: map.get(v.$font-weights, 'bold');
  234. font-size: map.get(v.$font-sizes, 'lg');
  235. margin-bottom: map.get(v.$spacers, 3);
  236. }
  237. }
  238. // Most Read Posts
  239. .popular-posts {
  240. background-color: var(--color-surface);
  241. .popular-post {
  242. @include mix.flex(row, flex-start, flex-start);
  243. gap: map.get(v.$spacers, 3);
  244. padding: map.get(v.$spacers, 3) 0;
  245. border-bottom: 1px solid v.$light-gray;
  246. @include mix.transition(v.$transition-base);
  247. &:last-child {
  248. border-bottom: none;
  249. padding-bottom: 0;
  250. }
  251. &:first-child {
  252. padding-top: 0;
  253. }
  254. &:hover {
  255. padding-left: map.get(v.$spacers, 2);
  256. @include mix.border-radius(v.$border-radius);
  257. }
  258. // Responsive: Stack vertically on tablet and mobile
  259. @include mix.respond-to-max('md') {
  260. @include mix.flex(column, flex-start, flex-start);
  261. gap: map.get(v.$spacers, 2);
  262. }
  263. }
  264. .post-thumbnail {
  265. flex-shrink: 0;
  266. img {
  267. width: 80px;
  268. height: 60px;
  269. object-fit: cover;
  270. @include mix.border-radius(v.$border-radius);
  271. @include mix.transition(v.$transition-base);
  272. &:hover {
  273. @include mix.transform(scale(1.05));
  274. }
  275. // Full width on tablet and mobile
  276. @include mix.respond-to-max('md') {
  277. width: 100%;
  278. height: 120px;
  279. }
  280. }
  281. // Full width container on tablet and mobile
  282. @include mix.respond-to-max('md') {
  283. width: 100%;
  284. }
  285. }
  286. .post-content {
  287. flex: 1;
  288. }
  289. .post-title {
  290. font-size: map.get(v.$font-sizes, 'sm');
  291. font-weight: map.get(v.$font-weights, 'semibold');
  292. line-height: 1.4;
  293. margin-bottom: map.get(v.$spacers, 1);
  294. a {
  295. color: var(--color-surface-mcase);
  296. @include mix.transition(v.$transition-base);
  297. &:hover {
  298. color: v.$primary;
  299. }
  300. }
  301. }
  302. .post-meta {
  303. @include mix.flex(column, flex-start, flex-start);
  304. gap: 0.25rem;
  305. font-size: map.get(v.$font-sizes, 'xs');
  306. @include mix.respond-to-min('md') {
  307. @include mix.flex(row, space-between, center);
  308. }
  309. }
  310. }
  311. // Newsletter Widget
  312. .newsletter {
  313. p {
  314. color: v.$white;
  315. margin-bottom: map.get(v.$spacers, 3);
  316. opacity: 0.9;
  317. }
  318. .newsletter-input {
  319. border: none;
  320. @include mix.border-radius(v.$border-radius);
  321. font-size: map.get(v.$font-sizes, 'base');
  322. @include mix.transition(v.$transition-base);
  323. &:focus {
  324. outline: none;
  325. @include mix.box-shadow(0 0 0 3px color.scale(v.$white, $alpha: -70%));
  326. }
  327. &:hover {
  328. background-color: color.scale(v.$white, $lightness: -3%);
  329. }
  330. }
  331. .newsletter-btn {
  332. border: none;
  333. cursor: pointer;
  334. @include mix.border-radius(v.$border-radius);
  335. @include mix.transition(v.$transition-base);
  336. &:hover {
  337. @include mix.transform(translateY(-2px));
  338. @include mix.box-shadow(v.$box-shadow);
  339. background-color: color.scale(v.$white, $lightness: -5%) !important;
  340. }
  341. &:active {
  342. @include mix.transform(translateY(0));
  343. }
  344. }
  345. }
  346. // Categories Widget
  347. .categories {
  348. background-color: var(--color-surface);
  349. .categories-list {
  350. list-style: none;
  351. padding: 0;
  352. li {
  353. border-bottom: 1px solid v.$light-gray;
  354. &:last-child {
  355. border-bottom: none;
  356. }
  357. a {
  358. @include mix.flex(row, space-between, center);
  359. padding: map.get(v.$spacers, 2) 0;
  360. color: var(--color-text-secondary);
  361. @include mix.transition(v.$transition-base);
  362. @include mix.border-radius(v.$border-radius);
  363. &:hover {
  364. color: v.$primary;
  365. padding-left: map.get(v.$spacers, 2);
  366. background-color: color.scale(v.$primary, $lightness: 95%);
  367. }
  368. .count {
  369. color: v.$gray;
  370. font-size: map.get(v.$font-sizes, 'sm');
  371. @include mix.transition(v.$transition-base);
  372. }
  373. &:hover .count {
  374. color: color.scale(v.$primary, $lightness: -20%);
  375. }
  376. }
  377. }
  378. }
  379. }
  380. // ==========================================================================
  381. // Header and Footer Styles
  382. // ==========================================================================
  383. .header {
  384. .navbar {
  385. @include mix.transition(v.$transition-base);
  386. &:hover {
  387. @include mix.box-shadow(v.$box-shadow);
  388. }
  389. }
  390. .nav-link {
  391. @include mix.transition(v.$transition-base);
  392. &:hover {
  393. color: v.$primary;
  394. }
  395. &.active {
  396. color: v.$primary;
  397. font-weight: map.get(v.$font-weights, 'semibold');
  398. }
  399. }
  400. }
  401. .footer {
  402. .footer-links {
  403. list-style: none;
  404. padding: 0;
  405. li {
  406. margin-bottom: map.get(v.$spacers, 1);
  407. a {
  408. @include mix.transition(v.$transition-base);
  409. &:hover {
  410. color: color.scale(v.$white, $lightness: -20%) !important;
  411. }
  412. }
  413. }
  414. }
  415. .social-link {
  416. @include mix.transition(v.$transition-base);
  417. &:hover {
  418. color: v.$primary !important;
  419. @include mix.transform(translateY(-2px));
  420. }
  421. }
  422. }
  423. // ==========================================================================
  424. // Responsive Adjustments
  425. // ==========================================================================
  426. @include mix.respond-to-max('md') {
  427. .article-main {
  428. padding: map.get(v.$spacers, 3);
  429. }
  430. .article-title {
  431. font-size: map.get(v.$font-sizes, '2xl') !important;
  432. }
  433. .article-meta {
  434. @include mix.flex(column, flex-start, flex-start);
  435. gap: map.get(v.$spacers, 2);
  436. }
  437. }
  438. @include mix.respond-to-max('sm') {
  439. .article-showcase {
  440. padding: map.get(v.$spacers, 3) 0;
  441. }
  442. .article-layout {
  443. gap: map.get(v.$spacers, 3);
  444. }
  445. .popular-post {
  446. @include mix.flex(column, flex-start, flex-start);
  447. .post-thumbnail {
  448. width: 100%;
  449. img {
  450. width: 100%;
  451. height: 120px;
  452. }
  453. }
  454. }
  455. }