vp-hero.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <script setup lang="ts">
  2. import { type Ref, inject } from 'vue'
  3. import type { DefaultTheme } from 'vitepress/theme'
  4. import VPButton from 'vitepress/dist/client/theme-default/components/VPButton.vue'
  5. import Hero from './icons/hero.vue';
  6. export interface HeroAction {
  7. theme?: 'brand' | 'alt'
  8. text: string
  9. link: string
  10. target?: string
  11. rel?: string
  12. }
  13. defineProps<{
  14. name?: string
  15. text?: string
  16. tagline?: string
  17. image?: DefaultTheme.ThemeableImage
  18. actions?: HeroAction[]
  19. }>()
  20. const heroImageSlotExists = inject('hero-image-slot-exists') as Ref<boolean>
  21. </script>
  22. <template>
  23. <div class="VPHero" :class="{ 'has-image': image || heroImageSlotExists }">
  24. <div class="container">
  25. <div class="main">
  26. <slot name="home-hero-info-before" />
  27. <slot name="home-hero-info">
  28. <h1 v-if="name" class="name">
  29. <span v-html="name" class="clip"></span>
  30. </h1>
  31. <p v-if="text" v-html="text" class="text"></p>
  32. <p v-if="tagline" v-html="tagline" class="tagline"></p>
  33. </slot>
  34. <slot name="home-hero-info-after" />
  35. <div v-if="actions" class="actions">
  36. <div v-for="action in actions" :key="action.link" class="action">
  37. <VPButton
  38. tag="a"
  39. size="medium"
  40. :theme="action.theme"
  41. :text="action.text"
  42. :href="action.link"
  43. :target="action.target"
  44. :rel="action.rel"
  45. />
  46. </div>
  47. </div>
  48. <slot name="home-hero-actions-after" />
  49. </div>
  50. <div class="image">
  51. <div class="image-container">
  52. <div class="image-bg" />
  53. <slot name="home-hero-image">
  54. <Hero />
  55. </slot>
  56. </div>
  57. </div>
  58. </div>
  59. </div>
  60. </template>
  61. <style scoped>
  62. .VPHero {
  63. margin-top: calc((var(--vp-nav-height) + var(--vp-layout-top-height, 0px)) * -1);
  64. padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 48px) 24px 48px;
  65. }
  66. @media (min-width: 640px) {
  67. .VPHero {
  68. padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 48px 64px;
  69. }
  70. }
  71. @media (min-width: 960px) {
  72. .VPHero {
  73. padding: calc(var(--vp-nav-height) + var(--vp-layout-top-height, 0px) + 80px) 64px 64px;
  74. }
  75. }
  76. .container {
  77. display: flex;
  78. flex-direction: column;
  79. margin: 0 auto;
  80. max-width: 1152px;
  81. }
  82. @media (min-width: 960px) {
  83. .container {
  84. flex-direction: row;
  85. }
  86. }
  87. .main {
  88. position: relative;
  89. z-index: 10;
  90. order: 2;
  91. flex-grow: 1;
  92. flex-shrink: 0;
  93. }
  94. .VPHero.has-image .container {
  95. text-align: center;
  96. }
  97. @media (min-width: 960px) {
  98. .VPHero.has-image .container {
  99. text-align: left;
  100. }
  101. }
  102. @media (min-width: 960px) {
  103. .main {
  104. order: 1;
  105. width: calc((100% / 3) * 2);
  106. }
  107. .VPHero.has-image .main {
  108. max-width: 592px;
  109. }
  110. }
  111. .name,
  112. .text {
  113. max-width: 392px;
  114. letter-spacing: -0.4px;
  115. line-height: 40px;
  116. font-size: 32px;
  117. font-weight: 700;
  118. white-space: pre-wrap;
  119. }
  120. .VPHero.has-image .name,
  121. .VPHero.has-image .text {
  122. margin: 0 auto;
  123. }
  124. .name {
  125. color: var(--vp-home-hero-name-color);
  126. }
  127. .clip {
  128. background: var(--vp-home-hero-name-background);
  129. -webkit-background-clip: text;
  130. background-clip: text;
  131. -webkit-text-fill-color: var(--vp-home-hero-name-color);
  132. }
  133. @media (min-width: 640px) {
  134. .name,
  135. .text {
  136. max-width: 576px;
  137. line-height: 56px;
  138. font-size: 48px;
  139. }
  140. }
  141. @media (min-width: 960px) {
  142. .name,
  143. .text {
  144. line-height: 64px;
  145. font-size: 56px;
  146. }
  147. .VPHero.has-image .name,
  148. .VPHero.has-image .text {
  149. margin: 0;
  150. }
  151. }
  152. .tagline {
  153. padding-top: 8px;
  154. max-width: 392px;
  155. line-height: 28px;
  156. font-size: 18px;
  157. font-weight: 500;
  158. white-space: pre-wrap;
  159. color: var(--vp-c-text-2);
  160. }
  161. .VPHero.has-image .tagline {
  162. margin: 0 auto;
  163. }
  164. @media (min-width: 640px) {
  165. .tagline {
  166. padding-top: 12px;
  167. max-width: 576px;
  168. line-height: 32px;
  169. font-size: 20px;
  170. }
  171. }
  172. @media (min-width: 960px) {
  173. .tagline {
  174. line-height: 36px;
  175. font-size: 24px;
  176. }
  177. .VPHero.has-image .tagline {
  178. margin: 0;
  179. }
  180. }
  181. .actions {
  182. display: flex;
  183. flex-wrap: wrap;
  184. margin: -6px;
  185. padding-top: 24px;
  186. }
  187. .VPHero.has-image .actions {
  188. justify-content: center;
  189. }
  190. @media (min-width: 640px) {
  191. .actions {
  192. padding-top: 32px;
  193. }
  194. }
  195. @media (min-width: 960px) {
  196. .VPHero.has-image .actions {
  197. justify-content: flex-start;
  198. }
  199. }
  200. .action {
  201. flex-shrink: 0;
  202. padding: 6px;
  203. }
  204. .image {
  205. order: 1;
  206. margin: -76px -24px -48px;
  207. }
  208. @media (min-width: 640px) {
  209. .image {
  210. margin: -108px -24px -48px;
  211. }
  212. }
  213. @media (min-width: 960px) {
  214. .image {
  215. flex-grow: 1;
  216. order: 2;
  217. margin: 0;
  218. min-height: 100%;
  219. }
  220. }
  221. .image-container {
  222. position: relative;
  223. margin: 0 auto;
  224. width: 320px;
  225. height: 320px;
  226. }
  227. @media (min-width: 640px) {
  228. .image-container {
  229. width: 392px;
  230. height: 392px;
  231. }
  232. }
  233. @media (min-width: 960px) {
  234. .image-container {
  235. display: flex;
  236. justify-content: center;
  237. align-items: center;
  238. width: 100%;
  239. height: 100%;
  240. /*rtl:ignore*/
  241. transform: translate(-32px, -32px);
  242. }
  243. }
  244. .image-bg {
  245. position: absolute;
  246. top: 50%;
  247. /*rtl:ignore*/
  248. left: 50%;
  249. border-radius: 50%;
  250. width: 192px;
  251. height: 192px;
  252. background-image: var(--vp-home-hero-image-background-image);
  253. filter: var(--vp-home-hero-image-filter);
  254. /*rtl:ignore*/
  255. transform: translate(-50%, -50%);
  256. }
  257. @media (min-width: 640px) {
  258. .image-bg {
  259. width: 256px;
  260. height: 256px;
  261. }
  262. }
  263. @media (min-width: 960px) {
  264. .image-bg {
  265. width: 320px;
  266. height: 320px;
  267. }
  268. }
  269. :deep(.image-src) {
  270. position: absolute;
  271. top: 50%;
  272. /*rtl:ignore*/
  273. left: 50%;
  274. max-width: 192px;
  275. max-height: 192px;
  276. /*rtl:ignore*/
  277. transform: translate(-50%, -50%);
  278. }
  279. @media (min-width: 640px) {
  280. :deep(.image-src) {
  281. max-width: 256px;
  282. max-height: 256px;
  283. }
  284. }
  285. @media (min-width: 960px) {
  286. :deep(.image-src) {
  287. max-width: 320px;
  288. max-height: 320px;
  289. }
  290. }
  291. </style>