alexdinca / store-framework Goto Github PK
View Code? Open in Web Editor NEWHome Page: https://lab.github.com/vtex-trainings/store-framework
Home Page: https://lab.github.com/vtex-trainings/store-framework
We begin our journey with the classic "Hello, World!". In order to create something similar, we need to understand the Store Framework blocks and use one that allows us to create texts. This block is aptly called Rich Text.
The Rich Text is only one of the dozens of blocks available in Store Framework. It's a block that seems simple, but that allows a series of customizations meant to create texts. To start, any text written in Rich Text supports Markdown, making text styling much easier.
Looking at the block's documentation, we can find a technical specification. One of the sessions present is that of the Blocks API, in which we can see the list of all Rich Text properties (props). These properties are the main customization points of a block, which give a block its distinct visual and behavior, depending on how its been configured.
We'll start by customizing the home page. In your theme's /store/blocks
folder, you'll find a file called home.jsonc
. This file determines how the blocks you intend to use are organized. The language used in the layout composition is simple and based on JSON.
In home.jsonc
, you'll notice a block which is default in all themes, namely store.home
. This block determines which child blocks will be displayed on the home page.
{
"store.home": {
"blocks": []
}
...
}
Let's use Rich Text in its body:
{
"store.home": {
"blocks": [
"rich-text"
]
}
...
}
Thus, store.home
now knows that it needs to render a Rich Text. However, we're haven't yet specified which visual this Rich Text should adopt. For that, we'll need to define the block.
Defining a block must always be performed apart from any other block, at thee source level of the JSON file.
{
"store.home": {
"blocks": [
"rich-text" <----- Here the block is used within another
]
},
"rich-text": { <----- Here it's at source level
}
}
In the block's definition, you can set its behavior and visual. Customization points have to be used to achieve this, so we'll start off using the Rich Text props
:
{
"store.home": {
"blocks": [
"rich-text"
]
},
"rich-text": {
"props": {
}
}
}
Read through the Rich Text documentation one more time and let's define the props we'll use to customize the block.
We want to achieve a simple "Hello, World!", and looking at the props we notice one called: text
(Text written in markdown language to be displayed). This is the prop that determines which text will be displayed.
Including this prop, we now have the following:
{
"store.home": {
"blocks": [
"rich-text"
]
},
"rich-text": {
"props": {
"text": "Hello, World!"
}
}
}
Reading through the Markdown documentation, we learn that in order for a text to appear in italic, we just need to place that text between *
:
{
"store.home": {
"blocks": [
"rich-text"
]
},
"rich-text": {
"props": {
"text": "*Hello, World!*"
}
}
}
To center align the text, we can add the textPosition
prop and give it the CENTER
value:
{
"store.home": {
"blocks": [
"rich-text"
]
},
"rich-text": {
"props": {
"text": "*Hello, World!*",
"textPosition": "CENTER"
}
}
}
Define a Rich Text on your home page and create a bold "Hello, World!" that's right aligned.
ℹ️ Remember to access the Rich Text documentation if you have any questions during the activity.
If you're still unsure as to how to send your answers, click here.
Before getting down to business there are some important, frequently occurring concepts you need to familiarize yourself with.
Store Framework is a commerce low-code tool used for building unique and custom storefronts.
The building flow starts with a custom theme, which can be worked on in different workspaces, without impacting the production environment.
A theme is essentially an arrangement of blocks and their position. In a theme, you can define every customization, position and style for every block that theme encompasses. You can also declare new pages in a theme, pages that in the future may represent about us or promotional landing pages (such as mother's day, black Friday, cyber Monday pages). The theme's end result consists of the content it's comprised of, in addition to the storefront.
Blocks are the minimal abstraction in Store Framework. They declare small pieces that make up a store's layout. Although they may look simple at first, blocks are imbued with a high customization power, allowing us to achieve complex designs. There are a total of four block customization levels:
Secure work environments that show a very close copy of what is running in production, allowing themes to be developed further without affecting the store.
Tab Layout is layout structuring paradigm created in Store Framework to allow the creation of layouts with tabs.
We have the following two containers in this paradigm: tab-list
and tab-content
. Each of these containers contains the items that it's comprised of. In tab-list
, we have tab-list.item
, whereas in tab-content
, we have tab-content.item
, respectively.
We'll look at a tab layout implementation example below.
First, you need to declare the tab-layout
block in the desired template:
{
"store.custom#about-us": {
"blocks": [
...
"tab-layout"
]
}
}
Then, you need to declare a tab-list
and a tab-content
as tab-layout
's children:
...
"tab-layout": {
"children": [
"tab-list",
"tab-content"
]
}
Having done this, these two containers are components of our tab-layout
. The next step is to declare tab-list.item
and tab-content.item
as tab-list
's and tab-content
's children:
...
"tab-list": {
"children": [
"tab-list.item#1",
"tab-list.item#2"
]
}
...
"tab-content": {
"children": [
"tab-content.item#1",
"tab-content.item#2"
]
}
In the next step, we'll declare the tab-list.item
properties. The code below creates a tab interface similar to the image below:
The tabId
property is very important as it's the key that connect a tab-list.item
button with a tab-content.item
.
...
"tab-list.item#1": {
"props": {
"tabId": "majorAppliances",
"label": "Major Appliances",
"defaultActiveTab": true
}
},
"tab-list.item#2": {
"props": {
"tabId": "electronics",
"label": "Electronics"
}
}
Now, let's declare the children and props pertaining to tab-content.item
.
In the child array, you can include several blocks such as rich-text
, info-card
, image
, flex-layout
and etc.
In the tabId
prop, you need to include the same ids that were declared in tab-list.item
for the link between the tab and content to function properly.
...
"tab-content.item#1": {
"children": [
"rich-text#1"
],
"props": {
"tabId": "majorAppliances"
}
},
"tab-content.item#2": {
"children": [
"rich-text#2"
],
"props": {
"tabId": "electronics"
}
}
Lastly, you must declare your content's proprieties. In our example, we just placed a rich-text
in each tab-content.item
:
"rich-text#1": {
"props": {
"text": "Texto para Major Appliances",
"textPosition": "CENTER",
"font": "t-heading-3"
}
},
"rich-text#2": {
"props": {
"text": "Texto para Electronics",
"textPosition": "CENTER",
"font": "t-heading-3"
}
}
In this activity, we will create the simple structure of a tab layout, as shown below. Thereafter, we will add some content to give our page a custom style.
about-us.jsonc
, add a tab-layout#home
to the store.custom#about-us
template;tab-layout#home
block and add tab-list#home
and tab-content#home
as its children;tab-list#home
and add tab-list.item#home1
and tab-list.item#home2
as its children;tab-list.item#home1
so that the interface displays "Major Appliances". (Tip: Do not forget to include tabId
= "majorAppliances"
as well as the property defaultActiveTab
= true
to the props);tab-list.item#home2
's props so that the interface displays "Electronics". (Tip: Don't forget to include tabId
= "electronics"
to the props);tab-content#home
in your theme and add tab-content.item#home1
and tab-content.item#home2
as children;tab-content.item
, declare just one rich-text
as child (for example, rich-text#home1
and rich-text#home2
);tabId
prop for each tab-content.item
to create a link between the previously created tab-list
and tab-content
;rich-text
and declare its props according to the code below:"rich-text#home1": {
"props": {
"text": "Área do conteúdo da tab-list.item com tabId = majorAppliances",
"textPosition": "CENTER",
"font": "t-heading-3"
}
},
"rich-text#home2": {
"props": {
"text": "Área do conteúdo da tab-list.item com tabId = electronics",
"textPosition": "CENTER",
"font": "t-heading-3"
}
}
ℹ️ Remember to access the Tab Layout and Rich Text documentation for any questions during the activity.
If you're still unsure as to how to send your answers, click here.
Once the store's homepage is done, we can start working on a new store template: the product page. Product pages are probably the templates with the most blocks, which makes them extremely flexible and customizable.
Let's build a minimal product page, with only the bare essentials:
Most product blocks, unlike content blocks, are inserted into a certain context, making them a little bit "plug-n-play": placing product-images
on the product page will automatically lead to images being rendered on that page, the same being valid for price and name.
This doesn't mean that these blocks are less customizable, quite the opposite actually, as we'll soon see.
Build a product page using the following blocks in product.jsonc
and declare it in the store/blocks
folder: product-images
, product-price
, product-name
and buy-button
. We expect the structure to contain the following:
store.product
;{
"store.product": {
"children": [
"flex-layout.row#main"
]
}
}
"flex-layout.row#main": {
"props": {
"marginTop": 6
},
"children": [
"flex-layout.col#left",
"flex-layout.col#right"
]
}
product-images
;"flex-layout.col#left": {
"children": [
"product-images"
]
}
product-name
, product-price
and buy-button
;In addition, we want:
verticalAlign
and preventVerticalStretch
props in the Flex Layout Column documentation)product-price
to show the total savings and list price (showSavings
and showListPrice
)ℹ️ Remember to access the product-images
, product-price
, product-name
e buy-button
documentation in case you have any questions during your activity.
If you're still unsure as to how to send your answers, click here.
It's important to establish that is not an expository course. Its objective is to teach participants about the VTEX IO Store Framework based on practical applications/activities. Therefore, in order to advance, you'll need to invest some time and dedication into it.
At the beginning of the course, you'll receive a repository containing a minimum amount of code our wish is that, upon completing all the tasks laid before you, you would have transformed that code into a fully functional store.
The course is divided into chapters. As each chapter begins, you'll receive initial instructions, just as you've already received for this and previous chapters. You should read through all of the presented content and perform the minor task set out at the end of the chapter.
For your answers to be sent, follow these steps:
git clone
https://github.com/alexdinca/store-framework
Open
on the notification that appears in the lower right cornerstore-framework
;alexdinca/store-framework
and then on Install.After reading the entire content and performing the wanted task, you should send your answer according to the steps below:
Source Control
, in the VSCode side menu;+
in the CHANGES section;✓
(Commit);+ Create new branch...
;Once you complete this flow, our bot will let you know whether your answers were correct or not. If yes, the reply will look like this:
Thereafter, you'll receive another reply highlighting the next steps:
If you're familiar with git, this entire flow is equivalent to creating a branch with a predefined name, committing changes and then executing a push.
Throughout the course, you may run into difficulty and not manage to correctly complete the activity on your first try. When this happens, you'll receive a feedback message that will let you know where you failed:
You can submit as many answers and you want by simply retrying the entire process explained in the previous section.
You may see your course progress at any time by going back to the main page. There, you'll see every step, what you've already completed, along with a button that takes you back to where you last stopped:
Anytime you open the VSCode terminal, you can run a vtex link
to monitor the evolution of your project in https://{workspace}--{account}.myvtex.com
. Make sure that the solution is visually comparable to what was presented and that no link errors occurred.
Do not create issues and PRs during the course flow, since this can interfere with its functioning.
For your Mac setup video, click here
Before getting your hands dirty and learning more about VTEX IO's Store Framework, you'll need to set up a few basic configurations, such as:
Have a look at the step-by-step below for each of these configurations:
Install Git on your computer by clicking on the link below and selecting your operating system (Windows, MAC or Linux):
Toolbelt is a VTEX command line tool. It allows you to perform any activity on the platform, such as creating a new development workspace, logging into a VTEX account, developing new apps, or managing already existing ones, etc.
Since it's Toolbelt that establishes the communication between the developer and the platform, you'll need it in order to perform all the activities put forward during the Store Framework course.
npm i -g vtex
in your terminal if you're using Windows or yarn global add vtex
if you're using MAC;Your can run vtex-v
(Windows) or vtex
(MAC) to confirm whether the Toolbelt installation was as expected.
Once successfully installed, your next step is to log into a VTEX account.
Run vtex login VTEXaccount
in your terminal, replacing VTEXaccount
with the name of the account in which you want to work. For example, vtex login appliancetheme
.
After logging in, run vtex whoami
to confirm the account and workspace in which you find currently are.
Workspaces are nothing other than what the namesake suggests. On VTEX IO, accounts have three main workspace types, namely master, production and development.
The next step is to create a development workspace, which will allow you to play with the configurations throughout the course without altering the store's final public version.
vtex use workspace-name
, replacing workspace-name
with the desired name. For example, vtex use devworkspace
.After creating the workspace, you'll be able to access it at this link: https://{workspace}--{account}.myvtex.com
, replacing {workspace}
and {conta}
with the name of the previously created workspace and account. For example, https://devworkspace--appliancetheme.myvtex.com
After setting up the basic configurations, you're ready to start the course!
An Iframe is a HTML element that allows the incorporation of another HTML page to the current one. Therefore, using the Iframe block, you can embed content from other URLs and display them on your page. It's important to remember that URLs rendered by Iframe have their own context, with independent session history and DOMs.
WARNING: Iframes are only allowed on custom page templates.
The iframe
block has very simple properties:
src
: shows which URL should be rendered by the iframewidth
: pixel width of the iframe elementheight
: height of the iframe elementBelow, we can see an implementation example of the iframe
block:
"store.custom#about-us": {
"blocks": [
"flex-layout.row#about-us",
"iframe"
]
},
"iframe": {
"props": {
"src": "http://someURL.com/resource",
"width": 100,
"height": 200
}
}
Let's display an Instagram post on your store page:
rich-text
and include an iframe
block;iframe
props, display the following link content in a 800px-wide by 1000px-high container: https://www.instagram.com/p/B973Qp8BacC/embed
ℹ️ Remember to access the Iframe documentation if you have any questions during this activity.
If you're still unsure as to how to send your answers, click here.
The Flex Layout is a layout structuring paradigm created in Store Framework to allow the construction of complex layouts. This paradigm uses the concepts of lines and columns to define the desired block structure and positioning on a certain page.
There are two basic building blocks for each Flex Layout:
flex-layout.row
flex-layout.col
If you are already familiar with the Flexbox used in CSS, the Flex Layout should be easy to understand, since Flexbox is already used "under the hood" by flex-layout.row and flex-layout.col.
With the help of Flex Layout, you can create customized layouts, using the Flexbox structure of lines and columns.
Analyzing the block's documentation, we see that you can use any block array as Flex Layout's children
. In addition, you should always use flex-layout.row
and flex-layout.col
, and NEVER flex-layout
alone.
Below, we have an example of a flex layout comprised of a flex-layout.row
with two children: an info-card
and a rich-text
:
"flex-layout.row":{
"children": [
"info-card#rethink",
"rich-text#delete"
]
},
"info-card#rethink": {
"props": {
"imageUrl": "https://appliancetheme.vteximg.com.br/arquivos/utensilios-cozinha-min.png",
"isFullModeStyle": true,
"headline": "Time to rethink your kitchen",
"callToActionText": "Discover",
"textPosition": "center"
}
},
"rich-text#deletar": {
"props": {
"text": "I'll be deleted soon"
}
}
flex-layout.row
in the store.home
's template blocks
and then declare the blocks mentioned above in your home.jsonc
file.flex-layout.row
children, substituting the rich-text
block with a flex-layout.col
column.rich-text
block from your theme.flex-layout.col
block in your home.jsonc
file with two image components as children: image#electronics
and image#major-appliance
, in this order.image
blocks with the following props:...
"image#electronics": {
"props": {
"src": "https://appliancetheme.vteximg.com.br/assets/vtex.file-manager-graphql/images/electronics_banner___25d69b49f8224b369375e68513b4d593.png",
"maxWidth": "100%"
}
},
"image#major-appliance": {
"props": {
"src": "https://appliancetheme.vteximg.com.br/assets/vtex.file-manager-graphql/images/major_appliance_banner___bb10093866a127345ddfbcca3efa5022.png",
"maxWidth": "100%"
}
}
The result should be similar to this:
ℹ️ Remember to access the Flex Layout documentation in case you have any questions during the activity.
If you're still unsure as to how to send your answers, click here.
The next block that we'll use is the Shelf, on which we display a product collection. This session will show you how to render and configure this shelf on your store's homepage.
After a closer look at the Shelf documentation, we see that it's possible to configure which product collection we want to have displayed using either category
, specificationFilters
or collection
props, according to the products added in the catalog.
Other props help to configure how the items are displayed. It's important to remember that the shelf
component always requests that product-summary
type blocks be part of its composition. Check out the product-summary block's documentation for more info on how it functions.
Below, we have a Shelf implementation example:
{
"store.home": {
"blocks": [
...
"shelf"
]
},
...
"shelf": {
"blocks": ["product-summary.shelf"],
"props": {
"category": 1,
"orderBy": "OrderByTopSaleDESC",
"paginationDotsVisibility": "desktopOnly",
"productList": {
"maxItems": 10,
"itemsPerPage": 5,
"minItemsPerPage": 1,
"scroll": "BY_PAGE",
"arrows": true,
"titleText": "Top sellers"
}
}
},
"product-summary.shelf": {
"children": [
"product-summary-image",
"product-summary-add-to-list-button",
"product-summary-name",
"product-rating-inline",
"product-summary-space",
"product-summary-price",
"product-identifier.summary",
"product-summary-buy-button"
]
}
}
home.jsonc
, declare a shelf
component is the store.home
template;shelf.jsonc
file;shelf.jsonc
, define the shelf
block with all the props given in the example above;8
4
Note: It's important to remember that the product-summary.shelf
block is already declared in default.jsonc
. Therefore, it wasn't necessary to declare it during this activity.
ℹ️ Remember to access the Shelf's documentation if you have any questions during this activity.
The end result should be similar to the one below:
In case you're using your own VTEX account, make sure that the category 1
is functional and active in your environment.
If you're still unsure as to how to send your answers, click here.
As we've seen, Markdown is a user friendly language that can be easily converted to HTML. In this lesson, we'll see how it's possible to use this language in our Rich Text block to create interesting texts.
To include texts in the rich-text
block, you need to use the text
prop:
"rich-text#home1": {
"props": {
"text": "Meu texto",
"textPosition": "LEFT",
"textAlignment": "LEFT"
}
The text
prop accepts markdown format. However, if you want to write your text using this language, your code must be similar to the following:
```json
"rich-text#home1": {
"props": {
"text": "# My title h1 \n Insert a paragraph here \n ## My title h2 \n Insert the second paragraph here \n Include a list here \n - Item 1 \n - Item 2 \n - Item3",
"textPosition": "LEFT",
"textAlignment": "LEFT"
}
TIP: Always use the \n
command to skip lines when using markdown in the text
prop
Other properties of the rich-text
component can be found in the Store Framework official documentation
In about-us.jsonc
, change the text in tab-list.item#home1
so that an "About" appears in the first tab;
In the rich-text
content linked to this tab, use the text below:
# Our history \n ### We were born from an internal VTEX hackathon! \n That's right. VTEX's first Hackatheme (store theme hackathon) had 3 finalists. One of them was FlatFlat, the store that you're accessing now. FlatFlat was created by the engineers Afonso Praça and Sávio Muniz, together with designers Lucas Falcão and Augusto Barbosa, and new business director Maurício Baum. As the store was created by profissionals having the most diverse backgrounds, the result was an obvious one: they became the finalists with coolest layout among participants.
ℹ️ Remember to access the Rich Text documentation if you have any questions during the activity.
If you're still unsure as to how to send your answers, click here
An e-commerce's homepage is always the client's first contact with the brand. Therefore, it's common for retailers to want to establish direct communication with their users at this strategic point in time during navigation.
Several components in Store Framework serve this scenario, such as the Info Card, which we looked at previously, and the Rich Text.
As we have seen during step three, the Rich Text is responsible for transforming texts into HTML elements, having the big advantage of reading Markdown. This give the component the flexibility to accept various text structures, allowing retailers to build their communication in more direct and clear way.
Just as with its functionality, the Rich Text configuration is also simple.
The same way that "Hello, world!" was created, we can create a block implementation example using written text in markdown format. For example:
"rich-text": {
"props": {
"text": "# Your Coffee, Your Way \n ### New Coffee Makers Collection",
"textPosition": "CENTER",
"textAlignment": "CENTER"
}
},
As previously stated, using Markdown allows for more component flexibility. However, it may also lead to your alteration in your rendering, according to the device used by the user.
For example: the above sentence ( # Your Coffee, Your Way \n ### New Coffee Makers Collection
) can be use a markdown that's suitable for desktop, but not necessarily for mobile as well (whose screen size is smaller).
To resolve this scenario and make the behavior more adaptable to other devices, we should use the Responsive Layout.
First, we must declare the blocks in the store.home
template:
"responsive-layout.desktop#desktop", "responsive-layout.mobile#mobile"
Them, we must declare these blocks as follows:
...
"responsive-layout.desktop#desktop": {
"children": ["rich-text#desktop"]
},
"responsive-layout.mobile#mobile": {
"children": ["rich-text#mobile"]
},
"rich-text#desktop": {
"props": {
"text": "# Your Coffee, Your Way \n ### New Coffee Makers Collection",
"textPosition": "CENTER",
"textAlignment": "CENTER"
}
},
"rich-text#mobile": {
"props": {
"text": "# Your Coffee, Your Way \n ### New Coffee Makers Collection",
"textPosition": "CENTER",
"textAlignment": "CENTER"
}
}
As we interpret the code above, we can notice two Rich Text configurations, built based on responsive-layout.desktop#desktop
and responsive-layout.mobile#mobile
.
During this activity, we will play around a little with the Rich Text markdown and learn how to use it with the Image component. All the while using Responsive Layout, of course!
home.jsonc
and declare the responsive-layout
block in the store.home
template;rich-text#mobile
, alter the markdown of the first sentence to h3
and of the second to h4
;image#desktop
as a child of responsive-layout.desktop#desktop
. Repeat the process with image#mobile
and responsive-layout.mobile#mobile
;"image#desktop": {
"props": {
"src": "https://appliancetheme.vteximg.com.br/arquivos/Responsive-Image-Desktop.jpg?q=1",
"link": {
"url": "/small-appliances/coffee-makers"
} ,
"alt": "Coffee Makers Collection"
}
},
"image#mobile": {
"props": {
"src": "https://appliancetheme.vteximg.com.br/arquivos/Responsive-Image-Mobile.jpg?q=1",
"link": {
"url": "/small-appliances/coffee-makers"
} ,
"alt": "Coffee Makers Collection"
}
},
100%
.ℹ️ Remember to access the Responsive Layout documentation if you have any question during your activity.
If you're still unsure as to how to send your answers, click here.
In this step, we'll learn how to configure the store's first component: the Header.
The Header plays a very important role on the store's main page, being responsible for housing other blocks that are crucial to user browsing, such as the search bar and the menu.
The Header block is responsivo, meaning that it can be configured to adapt to different devices, be it desktop or mobile.
Below, we can see an implementation example:
{
"header": {
"blocks": [
"header-layout.desktop",
"header-layout.mobile"
]
},
"header.full": {
"blocks": [
"header-layout.desktop",
"header-layout.mobile"
]
},
"header-layout.desktop": {
"children": [
"header-row#notification",
"header-row#main"
]
},
"header-layout.mobile": {
"children": [
"header-row#notification",
"header-row#main-mobile",
"header-row#search"
]
},
}
We will now configure a Header for your store's main page from scratch, with a notification and search bar, logo, cart and login. We will not configure the menu for now, since we'll look at it in depth during our next activity.
To implement a Header containing all these blocks, we'll start out from the code mentioned above and thus be able to build a responsive header, adaptable to desktop and mobile users alike.
Unlike the behavior found in other blocks, the Header doesn't need to be declared in one your theme's templates, since it will rendered on every store page anyway. In this exercise, we will declare the header
block in the header.jsonc
file, which must be created in the store/blocks
folder.
Thereafter, declare the following block:
"header-row#notification": {
"children": [
"header-spacer",
"rich-text#header",
"header-spacer"
]
},
header-row#main
, giving it the following children: logo
, header-spacer
, search-bar
, minicart
and login
;header-row#main
block, declare the inverted
, sticky
and fullWidth
props with the following values: true
, true
and false
, respectively;"header-row#main-mobile": {
"children": [
"logo",
"header-spacer",
"minicart",
"login"
],
"props": {
"sticky": true,
"inverted":true
}
},
"header-row#search": {
"children": [
"search-bar"
],
"props": {
"sticky": true
}
},
"login":{
"props": {
"showIconProfile": true,
"iconLabel": "Login"
}
},
"logo":{
"props": {
"url": "https://appliancetheme.vteximg.com.br/assets/vtex.file-manager-graphql/images/flatflat___6081e50402943bcb11bc45a8e613aa72.png"
}
},
"header-row#notification"
): the Rich Text;"rich-text#header": {
"props": {
"text": "**Free Shipping on orders over $50**",
"textPosition": "CENTER"
}
}
<path fill="currentColor" d="M4,13H1c-0.552,0-1-0.448-1-1v0c0-0.552,0.448-1,1-1h3V13z"></path> <path fill="currentColor" d="M15,3H1C0.448,3,0,2.552,0,2v0c0-0.552,0.448-1,1-1h14c0.552,0,1,0.448,1,1v0C16,2.552,15.552,3,15,3z"></path> <path fill="currentColor" d="M4,8H1C0.448,8,0,7.552,0,7v0c0-0.552,0.448-1,1-1h3V8z"></path> <path fill="currentColor" d="M15.707,13.293l-2.274-2.274C13.785,10.424,14,9.74,14,9c0-2.206-1.794-4-4-4S6,6.794,6,9 s1.794,4,4,4c0.74,0,1.424-0.215,2.019-0.567l2.274,2.274L15.707,13.293z M10,11c-1.103,0-2-0.897-2-2s0.897-2,2-2s2,0.897,2,2 S11.103,11,10,11z"></path>
<path fill="currentColor" d="M15,6h-1.4l-2.7-5.4C10.6,0.1,10-0.1,9.6,0.1C9.1,0.4,8.9,1,9.1,1.4L11.4,6H4.6l2.3-4.6 c0.2-0.5,0-1.1-0.4-1.3C6-0.1,5.4,0.1,5.1,0.6L2.4,6H1c-1.1,0-1.1,1-0.9,1.4l3,8C3.2,15.7,3.6,16,4,16h8c0.4,0,0.8-0.3,0.9-0.6l3-8 C16.1,7,16,6,15,6z"></path>
After completing step 8, the new search bar and cart icon should be rendered on your store page as follows:
ℹ️ Remember to access the header documentation if you have any questions during the activity.
If you're still unsure as to how to send your answers, click here.
A store needs a good home page to engage users, increasing session time and therefore conversion chances. To achieve this, several elements need to be used, such as promotional banners, shelves with highlights, about us material, etc.
We have created the next block on the home page using a call to action. In Store Framework, we have a block designed for this purpose, called Info Card.
Using the Info Card, you can create images which have links and buttons (top or side of the block) that direct the user's flow (Call to action).
Looking at the documentation, we can see that:
isFullModeStyle
defines whether the Call to Action (CTA) is set above the banner;textPosition
defines the position of the text;textAlignment
defines the text's alignment;imageUrl
defines which image will be used as banner;headline
determines which text will be used as headline;callToActionMode
allows to choose the CTA mode as either a link or a button;callToActionText
defines the CTA text;callToActionUrl
determines the URL to which it redirects;We therefore have the following props:
{
"store.home": {
"blocks": [
"rich-text",
"info-card"
]
},
"rich-text": {
"props": {
"text": "*Hello, World!*",
"textPosition": "RIGHT"
}
},
"info-card": {
"props": {
"isFullModeStyle": false,
"textPosition": "right",
"imageUrl": "https://appliancetheme.vteximg.com.br/arquivos/cozinha-rosa-min.png",
"headline": "Vintage Pink",
"subhead": "Give your kitchen a boho style adding vintage apparels.<br>Available until January 2020.",
"callToActionMode": "button",
"callToActionText": "Explore",
"callToActionUrl": "/sale/d",
"textAlignment": "center"
}
}
}
You may have asked yourself:
"What if I wanted to have two different Info Cards?"
It's possible through block instancing.
All block have pre-established names, but you can create block instances and define different ways in which the block types appear. After each block has been defined, simply place an '#' with an arbitrary name that makes, for example:
{
"store.home": {
"blocks": [
"rich-text",
"info-card#button-right"
]
},
...
"info-card#button-right": {
"props": {
"isFullModeStyle": false,
"textPosition": "right",
"imageUrl": "https://appliancetheme.vteximg.com.br/arquivos/cozinha-rosa-min.png",
"headline": "Vintage Pink",
"subhead": "Give your kitchen a boho style adding vintage apparels.<br>Available until January 2020.",
"callToActionMode": "button",
"callToActionText": "Explore",
"callToActionUrl": "/sale/d",
"textAlignment": "center"
}
}
}
WARNING: Throughout the course, you will notice several
...
, which you should not copy as it represents the progress achieved during previous steps.
In the home.jsonc
file, based on the code above, create the info-card#button-left
right under the infocard: info-card#button-right
. This new infocard must implement the following:
Shining chrome
Go to Collection
https://appliancetheme.vteximg.com.br/arquivos/cozinha-cinza-min.png
Give your kitchen a cool style adding warm metallic finishes.<br>Available until January 2020.
textPosition
).The expected result will look similar to this:
ℹ️ Remember to access the Info Card documentation if you have any questions regarding the activity.
If you're still unsure as to how to send your answers, click here.
Taking a quick look at your online store, you'll notice that components have similar styles, even without applying any customization.
All of them, including the recently configured Info Card, share pre-established values for font, background color, main color, button format, etc.
This is due to the style.json
, the file responsible for declaring generic customization values for every Store Framework store.
To create a unique identity for your store's components, you can overwrite these values using CSS customizations.
Analyzing the recipe employed to customize stores using CSS, we can notice that several steps will be needed in order to apply your own style, such as:
CSS
folder, naming it vtex.{AppName}.css
.{CSSHandle} {
{PropriedadeDeCSS}: {ValorDesejado};
{PropriedadeDeCSS}: {ValorDesejado};
}
:global(vtex-{componentName})
..{CSSHandle}--{blockClass} {
{PropriedadeDeCSS}: {ValorDesejado};
{PropriedadeDeCSS}: {ValorDesejado};
}
To uncover a component's CSS Handles, such as the Info Card, simply access your documentation's Customization
section.
According to the description of CSS Handles and to the store customization recipe using CSS, we can implement a customized Info Card example, editing its title and call-to-action button configurations by adding the following code to the vtex.store-components.css
file, found in /styles/css
:
.infoCardHeadline {
font-family: serif;
font-size: 2.25rem;
font-weight: normal;
color: gray;
border: 2px solid black;
padding: 24px;
}
.infoCardCallActionContainer :global(.vtex-button) {
color: white;
background-color: gray;
border: transparent;
}
.infoCardCallActionContainer :global(.vtex-button):hover {
color: gray;
background-color: blue;
border: transparent;
}
You can check the effect of these changes by running the vtex link
command.
Next, we'll add a specific style to the vintage info card. To begin, add the blockClass
prop to info-card#button-right
, as shown below:
"info-card#button-right": {
"props": {
"isFullModeStyle": false,
"textPosition": "right",
"imageUrl": "https://appliancetheme.vteximg.com.br/arquivos/cozinha-rosa-min.png",
"headline": "Vintage Pink",
"subhead": "Give your kitchen a boho style adding vintage apparels.<br>Available until January 2020.",
"callToActionMode": "button",
"callToActionText": "Explore",
"callToActionUrl": "/sale/d",
"textAlignment": "center",
"blockClass": "vintage" // <------------
}
}
Thereafter, declare a background-color
for this specific info card in your CSS file:
.infoCardContainer--vintage {
background-color: #EDCFD1
}
Check out the result by linking your app.
vtex.store-components.css
file, copy the above-mentioned code and use it in your theme's CSS file, according to the recipe on store customizations using CSS;max-width
) of 1260px
for all info cards, a margin of 0 auto
and a padding of 0
.black
;bold
;white
.vintage
blockClass, apply a new block class called metal
to the info card info-card#button-left
and apply the #e1e1e1
background color to it.If you're still unsure as to how to send your answers, click here to go through that step again.
We've just implemented our product page and are moving on to the search page. Both are similar in the sense that both have blocks that are unique in this context. We will explore this block in a disorderly fashion for now, just to get an idea of its behavior. Then, we'll look at improving the layout.
Same as other templates, store.search
can also be flexible. To build a unique layout you'll need to use a block called search-result-layout
.
{
"store.search": {
"blocks": ["search-result-layout"]
}
}
The search-result-layout
, in turn, must receive 3 other blocks:
search-result-layout.desktop
search-result-layout.mobile
search-not-found-layout
As you've already notice, the first two define which layout will be displayed on desktop and mobile respectively, while the third defines the layout of the no results found search page.
{
"store.search": {
"blocks": ["search-result-layout"]
},
"search-result-layout": {
"blocks": [
"search-result-layout.desktop",
"search-result-layout.mobile",
"search-not-found-layout"
]
}
}
In the course, we'll focus on desktop layout implementation.
The search results documentation offers a good reference for blocks that can be use in a search context. This step will focus on highlighting the main ones:
breadcrumb.search
);search-title.v2
);total-products.v2
);order-by.v2
);search-fetch-more
);search-fetch-previous
);filter-navigator.v3
);search-content
)Although quite many, all these blocks are relatively simple and work very well without an express need to configure their props
.
Copy the code above in search.jsonc
and define a search-result-layout.desktop
with the following characteristics:
pagination
prop here);Having the following, in the order mentioned below:
breadcrumb.search
;search-title.v2
;total-products.v2
;order-by.v2
;search-fetch-previous
;search-content
;filter-navigator.v3
;search-fetch-more
.ℹ️ Remember to go through the Search Result documentation if you have any questions during the activity.
If you're still unsure as to how to send your answers, click here.
In the previous step, we looked at creating a custom template. In scenarios involving promotions and commemorative dates, it's quite common to create special landing pages.
We saw that the search deduces what is needed from the given context. On a custom page however, the context doesn't exist and you need to state which query should be performed to get the results. All this is made possible by the search-result-layout.customQuery
.
The query schema is one of the custom query search result props that allows you to control the search that the landing page should run. To know more the query schema's possibilities, read through its documentation.
store.custom#landing
) in routes.json
;"store.custom#landing": {
"path": "/landing"
}
search-landing.jsonc
in the blocks folder;store.custom#landing
;image
block as one of this template's blocks. This block must have minWidth
props of 100% and an image of your choosing.search-result-layout.customQuery
:{
"store.custom#landing": {
"blocks": [
"image#landingbanner",
"search-result-layout.customQuery"
]
}
}
search-result-layout.customQuery
block as the querySchema prop that:ft
as mapField.If you're still unsure as to how to send your answers, click here.
In the last step, we've looked at the search page and its main components. We also learned that the search layout works like any other block, with the added benefit of having all the flexibility it can muster. In this step, we'll create some lines and columns to improve the appearance of the created search.
search.jsonc
file, remove total-products.v2
and order-by.v2
from search-result-layout.desktop
.json ... "flex-layout.row#top": { "children":["total-products.v2", "order-by.v2"]} ...
search-content
and filter-navigator.v3
from search-result-layout.desktop
and create a results line;json { ... "search-result-layout.desktop": { "children": [ "breadcrumb.search", "search-title.v2", "flex-layout.row#top", "search-fetch-previous", "flex-layout.row#results", "search-fetch-more" ], "props": { "pagination": "show-more" } }, "flex-layout.row#results": { "children": [ "flex-layout.col#filter", "flex-layout.col#search" ] }, ... }
filter
column's width
prop to 20%
.filter-navigator.v3
again and, in the right, include search-content
.product-summary
) that we used to display search results on the shelf.search-content
with the gallery
and not-found
blocks: json { ... "search-content": { "blocks": ["gallery", "not-found"] } ... }
product-summary.shelf
in the Gallery's props: json { ... "search-content": { "blocks": ["gallery", "not-found"] }, "gallery": { "blocks": ["product-summary.shelf"] } ... }
product-summary.shelf
block, found in default.jsonc
, include product-summary-sku-selector
above product-summary-buy-button
.ℹ️ Remember to access the Flex Layout documentation if you have any questions during the activity.
If you're still unsure as to how to send your answers, click here.
Stores are made up of several different pages, each with a specific content and layout. When creating a store from scratch in VTEX IO, some default pages with preset URLs are already available. Below, we have a list of some of these default pages:
store.home
�(Home page)store.product
(Product page)store.search
(Search Results page)store.account
(Client Account page)store.login
(Login page)store.orderplaced
(Order Placed page)But it's also possible for you to create a custom landing page. In this case, you should create a new URL and specific content to display to users that access this path.
Just a few steps a needed to create a custom landing page:
A template defines the page layout. However, if you want to create a custom page, you will also need to create a new template in your theme.
Let's imagine that your wanted to created a simple page containing information about your store. Inside the blocks
folder, you can create a file that would contain the following code, declaring a new template for a custom page,
{
"store.custom#{templatename}": {
"blocks": [
]
}
}
where {templateName}
must be replaced with the template's identifying name.
Then, you should fill in the code with the components needed to create the layout. Below we can an example of such implementation:
{
"store.custom#{templatename}": {
"blocks": [
"flex-layout.row#about-us"
]
},
"flex-layout.row#about-us": {
"children": [
"image#about-us",
"flex-layout.col#text-about-us"
]
},
"flex-layout.col#text-about-us": {
"children": [
"rich-text#about-title",
"rich-text#about-content"
],
"props": {
"preventVerticalStretch": true
}
},
"rich-text#about-title": {
"props": {
"text":
"# About FlatFlat"
}
},
"rich-text#about-content": {
"props": {
"text":
" FlatFlat is an electronics store with a long standing tradition for creating modern and vintage items. Out objective is to create home appliances that make your house stand out, no matter your style. Merely 2 months old, we're already the store with the most beautiful products among all VTEX stores. We are currently building our site with the aim of giving our customers an unforgetable experience with our brand!"
}
},
"image#about-us": {
"props": {
"src": "https://appliancetheme.vteximg.com.br/arquivos/cozinha-about-us.png",
"maxHeight": "600px"
}
}
}
Now that a new template with the page layout has been defined in the store theme's code, the next step is to establish the page's page that would lead to this layout.
We must create a routes.json
file in your theme's store
folder. Afterwards, insert the code below,
{
"store.custom#about-us": {
"path": "/{URL}"
}
}
where {URL}
is the name of the desired path.
Let's create a page containing information about your store, as in the example below:
blocks
folder, create a file called about-us.jsonc
;store.custom#about-us
template in this file;flex-layout.row
, use the code in example given above to complete the page layout;store
folder, create a file called routes.json
;/about-us
path;{workspace}--appliancetheme.myvtex.com/about-us
to see your new landing page.If you're still unsure as to how to send your answers, click here.
In this step we'll finish setting up our product page. We've learned how to stack blocks using Stack Layout, and we've also learned how to suggest similar product and better customize the SKU Selector for products with SKU images.
Use the following product URL to test this step: /3-colors-retro-stand-mixer/p
.
stack-layout
is a type of layout that allows blocks to stack on top of others. It comes in handy when you want to a badge on top of an image or product. It's also useful when placing rich text onto images (using a rich-text
and an image
).
(In the image, a shelf is stacked on top a carrossel 👆)
In this step, we'll use stack-layout
to place the brand on top of product images.
Develop the product page by adding the step below to product.jsonc
:
shelf.relatedProducts
under the product's main line"store.product": {
"children": [
"breadcrumb",
"flex-layout.row#main",
"shelf.relatedProducts"
]
}
/3-colors-retro-stand-mixer/p
product.
product-images
with a stack-layout
declaration;"flex-layout.col#left": {
"children": [
"stack-layout#brand"
]
}
stack-layout
and set product-images
and product-brand
as children;"stack-layout#brand": {
"children": [
"product-images",
"product-brand"
]
}
Consult the documentation to change the way in which product-brand
is displayed. You should make the logo appear with a height of 30 and include the displayMode: "logo"
prop.
Review the documentation to make the sku-selector
do the following:
ℹ️ Remember to read through the Stack Layout documentation if you have any questions during the activity.
ℹ️ Remember to use the /3-colors-retro-stand-mixer/p
product to test this step.
If you're still unsure as to how to send your answers, click here.
During the last step we learned how to create a simple product page with a minimum amount of products, but we know that the result is far from an ideal product page, so we'll add other elements that frequently appear on product pages from various stores.
Our documentation contains more than 30 product-related blocks. At the beginning of the course we looked at the Shelf and its related blocks, in addition to the 4 blocks we looked at during our last session. We'll explore 4 more in this step:
It's important that by the end of the course you take some time to fully explore our components, in addition to the customization possibilities that accompany each component.
Develop the product page by adding the 4 blocks listed above toproduct.jsonc
as follows:
breadcrumb
right before the product's main line;"store.product": {
"children": [
"breadcrumb",
"flex-layout.row#main"
]
}
product-identifier.product
right under the product-name
;sku-selector
and product-quantity
as children;{
...
"children": [
"product-price",
"flex-layout.row#qty-sku"
]
},
"flex-layout.row#qty-sku": {
"children": [
"sku-selector",
"product-quantity"
]
},
...
}
shipping-simulator
right under the line containing the SKU Selector and Product Quantityℹ️ Remember to access the Breadcrumb, Product Identifier, Product Quantity and SKU Selector documentation if you have any questions during the activity.
If you're still unsure as to how to send your answers, click here.
Having already configured and implemented the header, let's now add it to the Menu. Configuring the Menu is an important step in building your theme, since it's the block that's responsible for the user's navigation in your store. Because of its function and relevance, the Menu possesses a complex hierarchical structure, which involves menu items, submenus and, based on the latter, any Store Framework block (such as other Menus e Menu Items). Below we can see an image highlighting the Menu block's structure in practice:
![menu](https://user-images.githubusercontent.com/52087100/70004800-5cf9f300-1546-11ea-81fc-369e4bb58ed5.png)
When compared to some of the other blocks, the Menu's configuration may seem more difficult due to its structure of menus, menu items and submenus. However the Menu does not necessarily need to be configured according to the most complex scenario. Below, we can check a basic implementation example for the block, containing just the following 3 Menu Items: json { "[email protected]:menu#categories": { "children": \[ "menu-item#major-appliances", "menu-item#small-appliances", "menu-item#electronics" \], "props":{ "orientation": "horizontal" } } }, \
This activity will help us understand more about configuring the [Menu](https://vtex.io/docs/components/all/vtex.menu/), in addition to its existing hierarchy based on building Menu Items and Submenus.
header-row#menu\
block: json "header-row#menu": { "children": \[ "header-spacer", "[email protected]:menu#categories", "header-spacer" \] }
,drawer
as the header-row#main-mobile
block's first child;menu.jsonc\
code to horizontally render the 3 main Menu items: json { "[email protected]:menu#categories": { "children": \[ "menu-item#major-appliances", "menu-item#small-appliances", "menu-item#electronics" \], "props":{ "orientation": "horizontal" } },
menu.jsonc
, according to the example below: json "[email protected]:submenu#major":{ "children":\[ "[email protected]:menu#major" \] },
[email protected]:menu#categories\
block, build a secondary Major Appliances Menu, declared in the last step in menu.jsonc
. You must set the orientation
prop value to vertical
and configure the following Menu Items in the block's children list: menu-item#refrigerators
, menu-item#ovens
and menu-item#washers
;json "[email protected]:submenu#small":{ "children":\[ "[email protected]:menu#small" \] },
menu.jsonc
. As with the Major Appliances, you must set the orientation
prop value to vertical
and configure the following Menu Items in the block children list: menu-item#mixers
, menu-item#toasters
and menu-item#coffee
;menu-item#cameras
, menu-item#laptops
and menu-item#tvs
.ℹ️ Remember to access the Menu's [documentação](https://vtex.io/docs/components/all/vtex.menu/) if your have any questions during the activity.
Expected result: ![](https://appliancetheme.vteximg.com.br/arquivos/imagem-menu.png)
If you're still unsure as to how to send your answers, click here.
In addition to css, which we looked at previously, Store Framework offers another type of design customization, based on style.json
.
Every Store Framework block benefit from the same semantic styles definitions, based on Tachyons.
In practice, it means that instead of having to change all button backgrounds to use color that you want, you just need to redefine the color of an action-primary
background
. Customizations using style.json
tend a very big impact through css, since the store's visual identity across every page is usually maintained that way, without requiring a lot of changes. Therefore, use this tool whenever possible, thus avoiding unnecessary css overhead.
style.json
styles/configs/style.json
can be confusing at first, because it contains all the style definitions that every Store Framework visual block uses. However, inspecting browser elements is usually a good way to identify which styles to change. For example, right mouse click on any store button and select inspect.
Looking at the side bar that opened in Chrome, we notice a couple of definitions, one of them being the button background color (#0f3e99):
In addition, if you inspect while hovering, you'll notice the hover color (#072c75):
If we search the occurrences of both colors in style.json
, we uncover that the colors we inspected are actually used as action-primary
for the hover-background
and background
, for example. This gives us a better idea of where this definition may appear again.
The process to discovering text semantics with editable fields is the same as the above, we can search for attributes such as font size, weight and family. When inspecting a level 1 heading for example, we notice that its size is 3 rem.
1. In
style.json
, replace every color occurrence that you find, substituting: - #072c75 with #45a6a3 - #0F3E99 with #52BAB7
2. Change the font size for a level 1 heading so that its height is now 2.5 rem
If you're still unsure as to how to send your answers, click here.
As we've seen in the last step, a Submenu accepts any Store Framework block as child.
With this in mind, we can improve the previous activity's Menu configuration, adding to its content by using Flex Layout.
According to what we did in the last activity and what was learned about Flex Layout, let's apply Flex Layout to the Major Appliances submenu.
menu.jsonc
file, replace [email protected]:menu#major
with flex-layout.row#major
in the children list pertaining to the [email protected]:submenu#major
block;menu-flex.jsonc
and then define the following flex-layout.row#major
block:...
"flex-layout.row#major": {
"children": [
"flex-layout.col#menu",
"flex-layout.col#img"
]
},
flex-layout.row#major
. To start, declare the flex-layout.col#menu
block, having [email protected]:menu#major
as child;flex-layout.col#img
block, declaring it as image#menu
, while rich-text#header
is declared as child, in addition to the following props:...
"props":{
"paddingRight": 4,
"horizontalAlign": "right"
}
...
image#menu
as child in this last step, using the code below:...
"image#menu": {
"props": {
"src": "https://appliancetheme.vteximg.com.br/arquivos/menu-washer.jpg",
"link": {
"url": "/small-appliances/coffee-makers"
},
"alt": "Coffee Makers Collection",
"maxWidth": "200px"
}
}
ℹ️ Remember to access the documentation of both Flex Layout and Menu if you have any questions during the activity.
If you're still unsure as to how to send your answers, click here.
The Slider Layout, just as the Flex Layout, is a flexible way of creating a new block based on other blocks, using children
. It allows slider from other blocks to be created, such as info-card
and even flex-layout
, for example.
Let's use the Slider Layout to transform a couple of info-cards into a slide.
Looking at the documentation, we notice that we can use any block array as children
, just as with Flex Layout.
Below, you have an implementation example of a slider-layout with two info-card
:
"slider-layout#home": {
"children": ["info-card#1", "info-card#2"],
"props": {
"autoplay": {
"timeout": 5000,
"stopOnHover": false
}
}
},
"info-card#1": {
"props": {
"imageUrl": "https://images.unsplash.com/photo-1524185962737-ea7c028a12cd?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80",
"isFullModeStyle": true,
"headline": "Black Friday",
"callToActionText": "Subscribe",
"textPosition": "center"
}
},
"info-card#2": {
"props": {
"imageUrl": "https://images.unsplash.com/photo-1524185962737-ea7c028a12cd?ixlib=rb-1.2.1&auto=format&fit=crop&w=1350&q=80",
"isFullModeStyle": true,
"headline": "Black Friday",
"callToActionText": "Subscribe",
"textPosition": "center"
}
}
In this activity, we will create a brand slider for our site:
In home.jsonc
, declare the slider-layout#home
block to the store.home
template.
Create a file called slider-layout.jsonc
in the /store/blocks
folder;
In this file, based on the above-mentioned code, replace the declared info-card
as slider-layout#home
's children and add 6 image
image components as children. As format, use image#brand1
, image#brand2
(...) image#brand6
to declare the components;
Declare a specific src
prop for each defined image#brand
. Use the URLs below for each:
https://appliancetheme.vteximg.com.br/arquivos/flatflat-brand-logo-square1.png
https://appliancetheme.vteximg.com.br/arquivos/flatflat-brand-logo-square2.png
https://appliancetheme.vteximg.com.br/arquivos/flatflat-brand-logo-square3.png
https://appliancetheme.vteximg.com.br/arquivos/flatflat-brand-logo-square4.png
https://appliancetheme.vteximg.com.br/arquivos/flatflat-brand-logo-square5.png
https://appliancetheme.vteximg.com.br/arquivos/flatflat-brand-logo-square6.png
Lastly, you have to use the autoplay
property in the slider-layout#home
block. Make the slider occur automatically every 7 seconds and also make it stop when the user hovers over the slide.
ℹ️ Remember to go through the Slider Layout and Image documentation if you have any questions during the activity.
If you're still unsure as to how to send your answers, click here.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.