I have been trying to style the react component so that it looks like a horizontal bar chart. However, I am unable to align the centre div to the left as expected.
here is my react component for each row.
import React from 'react'
const level3UnitItem = ({ item, ratio }) => {
return (
<div className='country-div' >
<div className='name-div'> <h1>{item.country}</h1></div>
<div style={{ width: `${ratio * 400}px`, backgroundColor: 'red' }} className='bar-div'>
</div>
<div className='number-div'><h1>{item.population.toLocaleString('en-US')}</h1></div>
</div>
)
}
export default level3UnitItem
here is the css
.country-div{
width: 100%;
display: flex;
justify-content: space-between;
padding: 1rem;
margin: .2px;
}
.country-div h1, h2{
font-size: small;
}
and the current design is
I am expecting it to be
You will have to put 'name-div' and 'bar-div' inside another div(a parent div), like this:
import React from 'react'
const level3UnitItem = ({ item, ratio }) => {
return (
<div className='country-div' >
<div className="name-bar-container">
<div className='name-div'> <h1>{item.country}</h1></div>
<div style={{ width: `${ratio * 400}px`, backgroundColor: 'red' }} className='bar-div'>
</div>
</div>
<div className='number-div'><h1>{item.population.toLocaleString('en-US')}</h1></div>
</div>
)
}
export default level3UnitItem
CSS for 'name-bar-container' div:
.name-bar-container {
width: 100%;
display: flex;
}
you can also add margin-left to 'bar-div' to format it.
Can you try this? i add div inside the center div. then style it..
import React from 'react'
const level3UnitItem = ({ item, ratio }) => {
return (
<div className='country-div' >
<div className='name-div'> <h1>{item.country}</h1></div>
<div class="__toLeft">
<div style={{ width: `${ratio * 400}px`, backgroundColor: 'red' }} className='bar-div'>
</div>
</div>
<div className='number-div'><h1>{item.population.toLocaleString('en-US')}</h1>
</div>
</div>
)
}
export default level3UnitItem
and as for the css
.country-div{
width: 100%;
display: flex;
justify-content: space-between;
padding: 1rem;
margin: .2px;
}
.country-div h1, h2{
font-size: small;
}
.__toLeft {
display: flex;
justify-content: flex-start;
align-items: center;
}
I figured a a way to align those elements by wrapping the last two div(inside parent-div) into a new div and that solve my porblem.
import React from 'react'
const level3UnitItem = ({ item, ratio }) => {
return (
<div className='country-div' >
<div className='name-div'> <h1>{item.country}</h1></div>
<div className='barbar-div'>
<div style={{ width: `${ratio * 400}px`, backgroundColor: '#ffa500 ' }} className='bar-div' >
</div>
<div className='number-div'><h1>{item.population.toLocaleString('en-US')}</h1></div>
</div>
</div>
)
}
export default level3UnitItem
.country-div{
width: 100%;
display: flex;
justify-content: space-between;
padding: .3rem;
margin: .2px;
/* border: 1px solid black;
border-radius: .25rem; */
line-height: 2;
}
.country-div h1, h2{
font-size: small;
}
.name-div{
width: 300px;
}
.barbar-div{
width: 100%;
display: flex;
justify-content: space-between;
}
1. Create a container element and set its display property to "flex" to make it a flex container.
2. Create the individual flex items that will represent the bars of the chart. These can be div elements or any other HTML elements you prefer.
3. Set the width of each flex item to the appropriate percentage based on the data you want to represent in the chart. For example, if you want to represent a 50% value, set the width of the flex item to 50%.
4. Set the height of each flex item to a fixed value, such as 20px, to create a consistent height for all the bars in the chart.
5. Use the align-items property on the flex container to align the bars vertically. For example, use "align-items: center" to center the bars vertically in the container.
6. Use the justify-content property on the flex container to align the bars horizontally. For example, use "justify-content: space-between" to distribute the bars evenly across the container.
7. Use CSS to add colors and other styling to the flex items to make them look like bars in a chart.
8. Finally, add labels and other text elements to the chart to provide context and information about the data.
Note: To make the chart responsive, you can use media queries to adjust the width and height of the flex items and container based on the screen size.
Related
I have a simple page, which consists of a header, body, and footer. During the development, I want the content to have as much height as possible so that the entire page will have 100vh. The problem is that if I only put some plain text content as the body(so no children tags for the body), it will look like this:
It's wrong because then I will have to add height: 100%; width: 100%; in every children's style to make use of the full height of its parent. (imaging I pass a <div className=...>...</div> as the children)
This is the page.tsx:
import Footer from '#/components/Page/Footer'
import Header from '#/components/Page/Header'
import styles from './index.module.css'
interface PageProps {
children: any
}
function Page({ children }: PageProps) {
return (
<div className={styles.page}>
<Header />
{children}
<Footer />
</div>
)
}
export default Page
I already made .page:
.page {
display: flex;
flex-direction: column;
height: 100vh;
width: 100vw;
}
Seems like you want the children content inside to grow and fill up all the available spaces within its display: flex parent, it will be a good time to use flex-grow property.
Try this:
import Footer from '#/components/Page/Footer'
import Header from '#/components/Page/Header'
import styles from './index.module.css'
interface PageProps {
children: any
}
function Page({ children }: PageProps) {
return (
<div className={styles.page}>
<Header />
<div className={styles.childrenContainer}>
{children}
</div>
<Footer />
</div>
)
}
export default Page
.page {
display: flex;
flex-direction: column;
height: 100vh;
width: 100vw;
}
.childrenContainer{
flex-grow: 1;
}
One solution is to insert justify-content: space-between; on the page class, and margin-bottom:auto for your content to stick to your header.
I have a row of reactstrap cards that are dynamically placed in rows, and for "md" screen sizes and up (bootstrap), there will be 3 cards per row, and for lower it'll be 2 cards.
Here's the component:
<Card
outline
as='a'
style={{ cursor: 'pointer', margin: '1rem' }}
>
<CardImg top width='100%' src={img}' />
<CardBody>
<CardTitle tag='h5'>{this.props.title}</CardTitle>
</CardBody>
</Card>
My problem is, I want only the middle card to have margins on the sides (marginLeft: '1rem', marginRight: '1rem').
Since the number of cards in a row changes based on screensize, I can't just say "if it's a multiple of x, then have this style". Unless I know the screensize, so is there a way to create a prop in my parent component that I can pass into my card component to let it know what to set the margin as?
Thanks
(Any better suggestions are welcome)
More code:
render () {
return (
...
<div className='row' style={{margin:'0rem'}}>
{
disp.map((d, index) => {
return (
<div className='col-md-4 col-6 d-flex'>
<the card component here>
</div
)
...
}
}
</div>
....
If you want space in between the children then you can use gap and using flexbox. But gap property doesn't have great support.
html, body{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.card-container {
background-color: aquamarine;
display: flex;
flex-direction: column;
gap: 1rem;
}
.card {
border: 1px solid grey;
padding: 1rem;
height: 100px;
}
#media only screen and (min-width: 600px) {
.card-container {
flex-direction: row;
}
.card {
flex: 1;
}
}
<div class="card-container">
<div class="card">card1</div>
<div class="card">card1</div>
<div class="card">card1</div>
</div>
So you can use
[parent-selector] > * + *
Above selector means select all children that are siblings of any element and which is direct child of parent-selector(use class for parent or HTML element).
If the structure is from top to bottom or in row view then you can use
.card-container > * + * {
margin-top: 1rem;
}
and if you want space in between when the elements are arrange in left to right or in columns then use
.card-container > * + * {
margin-left: 1rem;
}
html, body{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.card-container {
background-color: aquamarine;
display: flex;
flex-direction: column;
}
.card {
border: 1px solid grey;
padding: 1rem;
height: 100px;
}
.card-container>*+* {
margin-top: 1rem;
}
#media only screen and (min-width: 600px) {
.card-container {
flex-direction: row;
}
.card {
flex: 1;
}
.card-container>*+* {
margin-top: 0;
margin-left: 1rem;
}
}
<div class="card-container">
<div class="card">card1</div>
<div class="card">card1</div>
<div class="card">card1</div>
</div>
"How to change margin size based on screen size breakpoints?"
That's exactly what the Bootstrap responsive spacing classes are used for. But, if you're using the grid (Row > Col) it's better to use padding (not margins) to adjust the spacing between columns.
Assuming you're using the column classes col-6 col-md-4 to get "for "md" screen sizes and up, there will be 3 cards per row, and for lower it'll be 2 cards."...
You can responsively adjust the padding on the columns, or the margin in the cards. For example, here's 3 margin units on md and up (mx-md-3), and 1 margin unit (mx-1) on lower:
<Col className="col-6 col-md-4 py-3">
<Card className="mx-1 mx-md-3">
...
</Card>
</Col>
React on Codeply
I have a flexbox. Its contents are NxN squares. I want the container to fit as many of these squares as possible given the display width. I want the flexbox to be center-aligned on the page.
However the problem is when I use
justify-content: center
image:
then the last row is not aligned to the left. However if I change to
justify-content: left
image:
then the entire container is no longer displayed center-aligned on the page. Can I somehow achieve a mix of two, so that is this example I would have centrally aligned 5 items, but the last row would be aligned to the first item in previous rows?
Minimal repro:
<style>
div { display: flex; flex-wrap: wrap; justify-content: center; }
i { display: block; width: 300px; height: 300px; background: black; margin: 10px; }
</style>
<div>
<i></i> <i></i> <i></i> <i></i> <i></i>
<i></i> <i></i> <i></i>
</div>
The layout you're building is a pretty standard grid. CSS grid is going to be a better fit than flexbox.
By using auto-fit for the columns and setting each to a fixed size, it will fit as many columns as it can within the container. justify-content: center will center your columns but content will still move across those columns from left to right.
Example:
div {
display: grid;
gap: 10px;
grid-template-columns: repeat(auto-fit, 160px);
justify-content: center;
}
span {
background: red;
display: block;
height: 160px;
}
<div>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
Found the answer here
Adding below CSS works
div:after {
content: '';
flex: auto;
margin: 10px;
max-width: 300px;
}
But, It breaks the responsiveness for Ipad and mobile screens, You can fix that by adding the above CSS only for big screens using a media query.
I would not recommend this, If possible switch flex-box to grid as stated by #nathan
Here is a method of achieving the asked question. I'm using Angular (Typescript) but the principle remains the same.
Firstly you'll need these two methods / functions:
/**
* Returns the width of each item in the flexfox (they need to be equal width!)
*/
getFlexboxItemWidth(selector: string){
return document.querySelector(selector)?.getBoundingClientRect().width
}
/**
* Returns the amount of additional, invisible flexbox items to place
*/
calculateFlexboxFillerItemCount(totalItems: number, selector: string, flexboxSelector: string): number{
const flexboxWidth: number = document.querySelector(flexboxSelector)?.getBoundingClientRect().width as number
const itemsPerRow: number = Math.trunc(flexboxWidth / (this.getFlexboxItemWidth(selector) as number))
const itemsInLastIncompleteRow: number = totalItems % itemsPerRow
return isNaN(itemsPerRow)
|| isNaN(itemsInLastIncompleteRow)
|| itemsPerRow - itemsInLastIncompleteRow === Infinity
|| itemsInLastIncompleteRow === 0
? 0
: itemsPerRow - itemsInLastIncompleteRow + 1
}
Once you have created those two methods you'll need to implement them:
<!--The Flexbox-->
<ion-list id="home-tab--item-list-container" *ngIf="items.length > 0">
<!--Visible Flex Items-->
<ion-card class="home-tab--item-card" *ngFor="let item of items" (click)="goToItemDetails(item)">
<img [src]="item.images[0]">
<ion-card-header>
<ion-card-title>{{item.title}}</ion-card-title>
</ion-card-header>
<ion-card-content>
{{item.description}}
<div class="home-tab--item-slide-price">
${{item.cost.toFixed(2)}}
<div class="home-tab--item-rental-period" *ngIf="item.rentalPeriod !== null">
/{{item.rentalPeriod}}
</div>
</div>
</ion-card-content>
</ion-card>
<!--Invisible Flexbox Items responsible for filling remaining space of last row-->
<ion-card [ngStyle]="{width: getFlexboxItemWidth('.home-tab--item-card')+'px', visibility: 'hidden'}" *ngFor="let _ of [].constructor(calculateFlexboxFillerItemCount(items.length, '.home-tab--item-card', '#home-tab--item-list-container'))">
</ion-card>
</ion-list>
And finally, here is the styling for better context:
#home-tab--item-list-container {
display: flex;
justify-content: center;
flex-wrap: wrap;
width: 100%;
overflow: auto;
}
.home-tab--item-card {
min-width: 170px;
max-width: 300px;
cursor: pointer;
}
#media screen and ( max-width: 960px) {
.home-tab--item-card { flex: 0 1 30% }
}
End Result:
I'm having a bit of trouble to produce the below with flex box. I'd like a centrally aligned "title" with some buttons to the right (2,3,4).
The code below gets me close, but it's not perfectly aligned and loses it when the window resizes.
Any suggestions?
.header {
display: flex;
height: 50px;
align-items: center;
justify-content: center;
}
.title {
width: 250px;
margin-left: auto;
margin-right: 15%;
}
.btn-group {
margin-right: 15%;
}
<div class="header">
<h1 class="title"></h1>
<div class="btn-group">
<button id="btn_1" class="selected">2</button>
<button id="btn_2">3</button>
<button id="btn_3">4</button>
</div>
</div>
Here's a clean and simple process to get you to your layout:
First, note that CSS pseudo-elements (i.e., ::before and ::after), when applied to flex containers, are treated as flex items.
Create a pseudo-element to serve as the first flex item in the container.
Make the pseudo consume all available space (i.e., set it to flex: 1)
Do the same with your button group (.btn-group) on the opposite end (i.e., set it to flex: 1)
Now, with the outer items pressuring from both sides, the title is pinned to the middle of the container.
Make the button group container a flex container.
Set that container to justify-content: center.
Now, the individual buttons are horizontally centered on the right side of the already centered title.
.header {
display: flex;
height: 50px;
align-items: center;
}
.header::before {
content: "";
flex: 1;
}
.btn-group {
flex: 1;
display: flex;
justify-content: center;
}
<div class="header">
<h1 class="title">1</h1>
<div class="btn-group">
<button id="btn_1" class="selected">2</button>
<button id="btn_2">3</button>
<button id="btn_3">4</button>
</div>
</div>
To better understand the concepts and methodology at work here, see this post:
Center and right align flexbox elements
Here are my suggestions when using flexbox layout. You do not need to set the width on the element because the width will resize dynamically. When you set display as flex in the container, the x-axis would change to row by default then use flex property for 'title' class to expand the width to double the width of 'btn-group'. As the result, the second div will push all the way to the right and you can add the width of margin-right as how much you want it to be. Also, I would create another div after header and give it a class name as 'title' instead of giving it on h1. That way you would have two children that allow you to control it. See below how I fixed it:
h1 {
text-align: center;
}
.header {
border: 1px solid red;
display: flex;
align-items: center;
justify-content: center;
}
.title {
flex: 1;
}
<div class="header">
<div class="title">
<h1>This is a title</h1>
</div>
<div class="btn-group">
<button id="btn_1" class="selected">2</button>
<button id="btn_2">3</button>
<button id="btn_3">4</button>
</div>
</div>
I have a flexbox container containing 2 div items. The flexbox container is setup for a row that centers the content inside. I am trying to figure out a way to center the contents of the row on the first item, and then have the second item be to the right of the first centered item.
Below is what I ended having to to do: have a the first item centered using the flexbox, but then I have to absolute position the second item to the desired position. Is there a way to remove the absolute positioning of the second item and have it appear just to the right of the first item, which is centered?
HTML
<div class="center-container">
<div class="time">
<span id="time">00:00</span>
</div>
<div class="time-ampm">
<span id="time-ampm">XX</span>
</div>
</div>
CSS
.center-container {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
}
.time {
flex: 1;
font-size: 50px;
font-weight: 300;
color: #262626;
}
.time-ampm {
position: absolute;
top: 115px;
left: 288px;
/* flex: 1; <= this is what I want to use, not the absolute positioning used above */
font-size: 20px;
font-weight: 400;
color: #858585;
}
This line shows where the flexbox centers the content if both of the items are set using flexbox (not the absolute positioning as above)
This line shows where I want the flexbox to center the items (centered on the first item)
What about setting the offset item width to 0?
HTML:
<div class="container">
<div>12:20</div>
<div class="offset">AM</div>
</div>
And CSS:
.container {
display:flex;
justify-content: center;
align-items:baseline;
}
.offset {
width:0;
}
http://codepen.io/anon/pen/dopQqo