使用 Chakra UI 构建落地页 - 第一部分
这篇文章最初发布在我的博客上。
您可以在这里订阅并了解更多我的文章。
太长不看
我将教你如何使用 Chakra UI 设计系统在 React 中构建响应式落地页。这是第一部分,我们将设置落地页并构建首页横幅部分。
![]()
🆕 使用@chakra_ui在#react 中 构建响应式落地页
我正在准备一系列博客文章,讲解如何仅使用 Chakra UI 库从零开始构建一个基本的落地页
👇 订阅我的邮件列表即可收到通知 👇
raptis.wtf2020年7月31日 上午8:55
安装
我们将创建一个新项目,并将create-react-app其命名为 landing-page。
npx create-react-app landing-page
cd demo-app
接下来,我们将安装 Chakra UI 库及其依赖项。
yarn add @chakra-ui/core @emotion/core @emotion/styled emotion-theming
要使用 React 设置 Chakra UI,需要它的 ThemeProvider,还可以选择添加自定义主题。您可以查看我之前的文章了解安装步骤。
定义文件夹结构
关于理想的 React 文件夹结构一直存在很多讨论。我认为并不存在完美的文件夹结构,你只需要选择一个清晰易懂、符合你目标的结构即可。
我采用KISS 原则(“保持简单,笨蛋”)来构建文件夹结构。它由三个核心目录组成:pages、components和utils。
├─ public
├─ src
└─ components
└─ layouts
└─ sections
└─ ui
└─ utils
└─ App.js
└─ index.js
组件文件夹
该components文件夹包含三个子目录:
sections包含所有部分的文件夹(例如:Hero、Header、Footer )- 包含我们主要页面布局的文件夹
layouts(例如 LandingLayout、AuthLayout) ui包含所有没有业务逻辑或副作用的小型组件的文件夹(例如:Logo、按钮)
页面文件夹
在这个pages文件夹中,您可以将我们着陆页的所有页面放置进去,例如首页、关于我们、注册、登录等等。
每个页面都会渲染一个布局,并由多个部分组成。每个部分组件都以其主要变量(文本、图像、链接)作为 props,因此自定义页面非常容易。
工具文件夹
该utils文件夹包含所有函数助手(例如,我们的自定义主题)。
一般来说,当需要在多个地方使用特定功能时,最好创建一个辅助函数。
设置应用程序组件
在本教程中,由于我们将创建一个单页着陆页,因此路由是可选的。但是,大多数实际的着陆页都包含多个页面,所以我在这里设置了路由,以节省您的时间。
该App组件将负责我们应用程序的路由。当然,我们会选择最先进的路由react-router-dom库。
您可以通过输入以下命令进行安装:
yarn add react-router-dom
在本教程中,由于我们将创建一个单页着陆页,因此路由是可选的。但是,大多数实际的着陆页都包含多个页面,因此我在这里进行了设置,以方便您的操作。
我们的设置很简单。如果您需要了解更多高级细节,可以查看官方文档。
我们必须使用一个BrowserRouter组件来包装我们的应用程序,该组件可以使 UI 与 URL 保持同步。建议使用 BrowserRouter 而不是普通的路由,Router因为它会history在内部处理对象。
然后,我们定义Route组件(仅/用于我们的示例),并用组件包裹它们Switch。
Switch 组件选择与当前位置匹配的路径,并且只返回一个组件。
import React from "react"
import { BrowserRouter, Switch, Route } from "react-router-dom"
import Landing from "./pages/Landing"
export default function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/">
<Landing />
</Route>
</Switch>
</BrowserRouter>
)
}
创建布局
现在是时候创建LandingLayout.js文件并将其放入/components/layouts文件夹中了。
该组件将始终渲染页眉、页脚以及作为子组件传递的任何组件。
为了实现落地页的垂直布局,我们需要添加Flex Chakra UI 组件。它会以经典div元素的形式渲染display: flex。Flex 组件有一些方便的简写属性:
flexDirection是directionflexWrap是wrapalignItems是alignjustifyContent是justify
因此,初始的 LandingLayout 组件是一个列居中的 flexbox 组件,它渲染 Header 组件及其所有子组件。为了使布局居中并具有响应式设计,我们添加了CSS 样式并针对大屏幕margin: 0 auto进行了设置。max-width: 1200px
Chakra UI 提供了两种定义响应式样式的方法。您可以根据具体情况选择更合适、更易读的方案。
// First option
maxW={[
"auto", // base
"auto", // 480px upwards
"auto", // 768px upwards
"1200px", // 992px upwards
]}
// Second option
maxW={{
base: "auto",
sm: "auto",
md: "auto",
lg: "auto",
xl: "1200px"
}}
完整的LandingLayout组件如下:
import React from "react"
import { Flex } from "@chakra-ui/core"
import Header from "../sections/Header"
import Footer from "../sections/Footer" // will add this in the part 2
export default function LandingLayout(props) {
return (
<Flex
direction="column"
align="center"
maxW={{ xl: "1200px" }}
m="0 auto"
{...props}
>
<Header />
{props.children}
<Footer />
</Flex>
)
}
下一步是创建Header位于其内部的组件。LandingLayout.
响应式头部组件
文件Header.js将位于该/components/sections文件夹中。
该组件的起点是Jean Bauer 在 Chakra UI 官方文档中编写的这段代码。
我们将进行一些调整,使该组件完全响应式,并增强其用户界面。
最外层组件是一个行状的 flexboxnav元素。该justify属性设置为 true,space-between以便在徽标和实际菜单之间留出适当的间距。
此外,我们根据活动屏幕尺寸,将颜色background-color组合color规则设置为上图中显示的颜色组合。
<Flex
as="nav"
align="center"
justify="space-between"
wrap="wrap"
w="100%"
mb={8}
p={8}
bg={["primary.500", "primary.500", "transparent", "transparent"]}
color={["white", "white", "primary.700", "primary.700"]}
{...props}
>
...
</Flex>
display: block这里我们要应用的基本技巧是通过有条件地应用 CSS 规则来隐藏/显示菜单图标和菜单项display: none。
菜单/关闭图标仅在断点处可见base,在大于md断点的屏幕上隐藏。根据该show值,我们会显示CloseIcon(当show === true)或MenuIcon(当show === false)。
<Box display={{ base: "block", md: "none" }} onClick={toggleMenu}>
{show ? <CloseIcon /> : <MenuIcon />}
</Box>
菜单项也采用了同样的技巧。在大于md断点的屏幕上,菜单项始终显示;而在较小的屏幕上,菜单项则根据条件显示。显示条件取决于变量的状态show,该变量可通过点击“菜单/关闭”图标进行切换。
这里需要特别说明的是 CSS 属性的使用flex-basic。它用于设置 flex 元素的初始主尺寸。我们使用该属性来强制菜单图标出现时,元素换行显示。它与flex-wrap: wrap最外层 flex 组件的规则结合使用,该规则允许其子元素换行显示。
<Box
display={{ base: show ? "block" : "none", md: "block" }}
flexBasis={{ base: "100%", md: "auto" }}
>
...
</Box>
这个盒子里存放着我们的菜单。为了方便起见,我们将使用一个Flex容器来定义子元素的方向并调整它们的位置。
这里简单说明一下。我们本来可以选择 Stack 组件而不是 Flex 组件Stack。但在我们的例子中,Stack 组件引入了一些 UI 错误,所以我们选择了 Stack 组件Flex。
<Flex
align="center"
justify={["center", "space-between", "flex-end", "flex-end"]}
direction={["column", "row", "row", "row"]}
pt={[4, 4, 0, 0]}
>
...
</Flex>
对于菜单项,我们创建一个单独的MenuItem组件,该组件会渲染一个Text带有元素的组件Link到所需位置。
由于使用了Flex组件作为容器,我们需要手动设置菜单项之间的间距。
这是通过传递该属性实现的isLast。该属性指示是否需要向 MenuItem 添加适当的边距。
const MenuItem = ({ children, isLast, to = "/", ...rest }) => {
return (
<Text
mb={{ base: isLast ? 0 : 8, sm: 0 }}
mr={{ base: 0, sm: isLast ? 0 : 8 }}
display="block"
{...rest}
>
<Link to={to}>{children}</Link>
</Text>
)
}
最终的头部组件如下所示:
import React from "react"
import { Link } from "react-router-dom"
import { Box, Flex, Text, Button, Stack, PseudoBox } from "@chakra-ui/core"
import Logo from "../ui/Logo"
import { CloseIcon, MenuIcon } from ".../Icons"
const MenuItems = props => {
const { children, isLast, to = "/", ...rest } = props
return (
<Text
mb={{ base: isLast ? 0 : 8, sm: 0 }}
mr={{ base: 0, sm: isLast ? 0 : 8 }}
display="block"
{...rest}
>
<Link to={to}>{children}</Link>
</Text>
)
}
const Header = props => {
const [show, setShow] = React.useState(false)
const toggleMenu = () => setShow(!show)
return (
<Flex
as="nav"
align="center"
justify="space-between"
wrap="wrap"
w="100%"
mb={8}
p={8}
bg={["primary.500", "primary.500", "transparent", "transparent"]}
color={["white", "white", "primary.700", "primary.700"]}
{...props}
>
<Flex align="center">
<Logo
w="100px"
color={["white", "white", "primary.500", "primary.500"]}
/>
</Flex>
<Box display={{ base: "block", md: "none" }} onClick={toggleMenu}>
{show ? <CloseIcon /> : <MenuIcon />}
</Box>
<Box
display={{ base: show ? "block" : "none", md: "block" }}
flexBasis={{ base: "100%", md: "auto" }}
>
<Flex
align={["center", "center", "center", "center"]}
justify={["center", "space-between", "flex-end", "flex-end"]}
direction={["column", "row", "row", "row"]}
pt={[4, 4, 0, 0]}
>
<MenuItems to="/">Home</MenuItems>
<MenuItems to="/how">How It works </MenuItems>
<MenuItems to="/faetures">Features </MenuItems>
<MenuItems to="/pricing">Pricing </MenuItems>
<MenuItems to="/signup" isLast>
<Button
size="sm"
rounded="md"
color={["primary.500", "primary.500", "white", "white"]}
bg={["white", "white", "primary.500", "primary.500"]}
_hover={{
bg: [
"primary.100",
"primary.100",
"primary.600",
"primary.600",
],
}}
>
Create Account
</Button>
</MenuItems>
</Flex>
</Box>
</Flex>
)
}
export default Header
让我们深入了解一下英雄部分。
首页横幅部分是任何落地页最重要的部分。它是用户首先接触的部分,必须做到完美!
如下所示,该部分由两个核心元素组成:图片和主要内容(标题、副标题、行动号召按钮)。
在继续之前,我们需要在这里定义组件的属性。我们首页的主要变量有五个:标题、副标题和按钮的文本,以及图片和行动号召(CTA)链接的URL。
export default function Hero({
title,
subtitle,
image,
ctaLink,
ctaText,
...rest
}) {
return ();
}
关于实际代码,顶部容器将Flex再次成为一个组件。我们将根据屏幕尺寸更改其flex-direction属性justify-content。
对于移动设备,我们将方向设置为column-reverse。之所以这样选择,是因为我们想改变两个主要元素的顺序。
传递该属性是为了允许我们从外部rest操作组件的最外层容器。Hero
<Flex
align="center"
justify={{ base: "center", md: "space-around", xl: "space-between" }}
direction={{ base: "column-reverse", md: "row" }}
wrap="no-wrap"
minH="70vh"
px={8}
mb={16}
{...rest}
>
...
</Flex>
传递此min-height属性是为了确保该部分在大屏幕上垂直居中显示。您可以在下图看到区别。
现在,轮到图像组件了。唯一需要做的就是调整宽度。在小屏幕上,我们需要强制width: 80%并逐步缩小图像宽度。
此外,我们margin-bottom在小屏幕上添加了一个,以增大图像和内容之间的空间。
<Box w={{ base: "80%", sm: "60%", md: "50%" }} mb={{ base: 12, md: 0 }}>
<Image src={image} size="100%" rounded="1rem" shadow="2xl" />
</Box>
关于内容元素,它是一个简单的Stack元素,包含两个Heading组件,即按钮Button和Text按钮下方的文本组件。
这里唯一值得注意的地方是元素的排列方式,在移动设备上应该居中显示,而在大屏幕上则应该位于左侧。
<Stack
spacing={4}
w={{ base: "80%", md: "40%" }}
align={["center", "center", "flex-start", "flex-start"]}
>
<Heading
as="h1"
size="xl"
fontWeight="bold"
color="primary.800"
textAlign={["center", "center", "left", "left"]}
>
{title}
</Heading>
<Heading
as="h2"
size="md"
color="primary.800"
opacity="0.8"
fontWeight="normal"
lineHeight={1.5}
textAlign={["center", "center", "left", "left"]}
>
{subtitle}
</Heading>
<Link to={ctaLink}>
<Button
variantColor="primary"
borderRadius="8px"
py="4"
px="4"
lineHeight="1"
size="md"
rightIcon="chevron-right"
>
{ctaText}
</Button>
</Link>
<Text
fontSize="xs"
mt={2}
textAlign="center"
color="primary.800"
opacity="0.6"
>
No credit card required.
</Text>
</Stack>
Hero 组件已准备就绪,您可以在下方查看完整代码:
import React from "react"
import { Link } from "react-router-dom"
import PropTypes from "prop-types"
import { Box, Button, Flex, Image, Heading, Stack, Text } from "@chakra-ui/core"
export default function Hero({
title,
subtitle,
image,
ctaLink,
ctaText,
...rest
}) {
return (
<Flex
align="center"
justify={{ base: "center", md: "space-around", xl: "space-between" }}
direction={{ base: "column-reverse", md: "row" }}
wrap="no-wrap"
minH="70vh"
px={8}
mb={16}
{...rest}
>
<Stack
spacing={4}
w={{ base: "80%", md: "40%" }}
align={["center", "center", "flex-start", "flex-start"]}
>
<Heading
as="h1"
size="xl"
fontWeight="bold"
color="primary.800"
textAlign={["center", "center", "left", "left"]}
>
{title}
</Heading>
<Heading
as="h2"
size="md"
color="primary.800"
opacity="0.8"
fontWeight="normal"
lineHeight={1.5}
textAlign={["center", "center", "left", "left"]}
>
{subtitle}
</Heading>
<Link to={ctaLink}>
<Button
variantColor="primary"
borderRadius="8px"
py="4"
px="4"
lineHeight="1"
size="md"
rightIcon="chevron-right"
>
{ctaText}
</Button>
</Link>
<Text
fontSize="xs"
mt={2}
textAlign="center"
color="primary.800"
opacity="0.6"
>
No credit card required.
</Text>
</Stack>
<Box w={{ base: "80%", sm: "60%", md: "50%" }} mb={{ base: 12, md: 0 }}>
<Image src={image} size="100%" rounded="1rem" shadow="2xl" />
</Box>
</Flex>
)
}
Hero.propTypes = {
title: PropTypes.string,
subtitle: PropTypes.string,
image: PropTypes.string,
ctaText: PropTypes.string,
ctaLink: PropTypes.string,
}
Hero.defaultProps = {
title: "React landing page with Chakra UI",
subtitle:
"This is the subheader section where you describe the basic benefits of your product",
image: "https://source.unsplash.com/collection/404339/800x600",
ctaText: "Create your account now",
ctaLink: "/signup",
}
总结
目前为止,我们的着陆页应该看起来像这样!
下周,我们将构建响应式页脚和功能部分。
如果你喜欢这篇文章,可以关注我的推特账号,我每天都会分享关于编程、设计和微型创业公司启动的技巧。
文章来源:https://dev.to/d__raptis/build-a-landing-page-with-chakra-ui-part-1-56gd






