【CSS】 Grid布局:现代网页设计的基石

引言

最近接到一个网页布局比较复杂的页面,看了半天还是决定用grid布局来写,记录一下

布局是构建用户界面的关键部分。CSS Grid布局提供了一种简单而强大的方式来创建复杂的网格布局,它让设计师和开发者能够更直观、更灵活地控制网页的结构。本文将深入探讨CSS Grid布局的核心概念、优势以及如何在实际项目中应用它。

CSS Grid布局基础

什么是CSS Grid布局?

CSS Grid布局是一种基于网格的布局系统,允许你将页面分割成多个行和列。它提供了一种在二维空间内布局元素的方法,可以轻松地创建复杂的布局结构。

Grid容器和Grid项

  • Grid容器 :通过设置 display: griddisplay: inline-grid 属性,一个元素就变成了Grid容器。
  • Grid项:Grid容器的直接子元素自动成为Grid项。

Grid轨道

  • Grid列和行 :通过 grid-template-columnsgrid-template-rows 属性定义网格的列和行。
  • Grid间隙 :使用 grid-column-gapgrid-row-gap(或简写为 grid-gap)来设置网格项之间的间隙。

Grid线

  • 命名网格线:可以给网格线命名,以便更精确地定位Grid项。
  • 网格线编号:网格线从1开始编号,可以使用这些编号来指定Grid项的位置。

CSS Grid布局的优势

灵活性

CSS Grid布局提供了前所未有的灵活性,可以轻松地创建响应式设计,适应不同屏幕尺寸和设备。

简洁性

与传统的浮动布局相比,Grid布局的语法更加简洁明了,减少了需要的CSS代码量。

对齐控制

CSS Grid布局提供了强大的对齐控制功能,包括对齐网格项和对齐整个网格。

CSS Grid布局的实用技巧

创建复杂布局

介绍如何使用Grid布局创建复杂的页面布局,包括多列布局、不规则网格等。

响应式设计

探讨如何利用CSS Grid布局实现响应式设计,包括使用媒体查询和网格模板区域。

与Flexbox的对比

简要比较CSS Grid布局与Flexbox布局,帮助读者理解何时使用Grid布局,何时使用Flexbox布局。

实际案例分析

通过分析几个实际的网页设计案例,展示CSS Grid布局在实际项目中的应用。

结语

CSS Grid布局是现代网页设计不可或缺的一部分。掌握它将为你的网页设计带来无限可能,让你能够创建出既美观又功能强大的用户界面。

文章最后推荐一下:CSS Grid Generator

它可以帮你快速实现grid布局

文章开头布局,完整代码:

javascript 复制代码
<!-- 懒得简化了,先实现页面再说,时间紧任务重,哈哈哈 -->
<template>
  <div class="InvCurrTons">
    <div class="InvCurrTons-left">
      <div class="InvCurrTons-left-box1">
        <div class="toTitle">
          <h3>
            碎浆站
          </h3>
          <div class="fTitle">
            <div>短纤: 23323</div>
            <div>长纤: 42332</div>
            <div>机械浆: 432</div>
            <div>总计: 66087</div>
          </div>
        </div>
        <div class="InvCurrTons-left-box1-content">
          <div class="parent" style="width: 12%;">
            <div v-for="(item, index) in PulpData.items" :key="index">
              <!-- kd: item.type === 0, -->
              <div
                class="kd"
                :class="{
                  bd: item.type === 1,
                  md: item.type === 2
                }"
              >
                {{ item.name }}
              </div>
            </div>
          </div>
          <div class="vertical-dashed-line"></div>
          <div class="vertical-dashed-line2"></div>
          <div class="parent-right" style="width: 85%;">
            <div class="parent-right-item1">
              <div class="parent5">
                <div
                  class="box1 kd"
                  :class="{
                    bd: PulpData.items1[0].type === 1,
                    md: PulpData.items1[0].type === 2,
                    bd2: PulpData.items1[0].type === 3
                  }"
                >
                  {{ PulpData.items1[0].name }}
                </div>
              </div>
            </div>
            <div class="parent-right-item2">
              <div class="parent2" style="width: 45%;">
                <div
                  v-for="(item, index) in PulpData.items2"
                  :key="index"
                  :class="'LBDiv' + (index + 1)"
                >
                  <!-- kd: item.type === 0, -->
                  <div
                    class="kd"
                    :class="{
                      bd: item.type === 1,
                      md: item.type === 2,
                      bd2: item.type === 3
                    }"
                  >
                    {{ item.name }}
                  </div>
                </div>
              </div>

              <div
                class="vertical-dashed-line2"
                style="margin-left: 4px;"
              ></div>
              <div class="vertical-dashed-line2"></div>
              <div class="vertical-dashed-line3"></div>
              <div
                class="vertical-dashed-line3"
                style="width: 22%;left: 48%;"
              ></div>

              <div class="parent3" style="width: 20%;">
                <div
                  v-for="(item, index) in PulpData.items3"
                  :key="index"
                  :class="'CBDiv' + (index + 1)"
                >
                  <!-- kd: item.type === 0, -->
                  <div
                    class="kd"
                    :class="{
                      bd: item.type === 1,
                      md: item.type === 2,
                      bd2: item.type === 3
                    }"
                  >
                    {{ item.name }}
                  </div>
                </div>
              </div>

              <div class="vertical-dashed-line4"></div>
              <div class="vertical-dashed-line2"></div>
              <div class="vertical-dashed-line2"></div>

              <div class="parent4" style="width: 25%;">
                <div
                  v-for="(item, index) in PulpData.items4"
                  :key="index"
                  :class="'RBDiv' + (index + 1)"
                >
                  <!-- kd: item.type === 0, -->
                  <div
                    class="kd"
                    :class="{
                      bd: item.type === 1,
                      md: item.type === 2,
                      bd2: item.type === 3
                    }"
                  >
                    {{ item.name }}
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="InvCurrTons-left-box2">
        <div class="toTitle" style="height: 20%;">
          <h3>
            旧堆场
          </h3>
          <div class="fTitle">
            <div>短纤: 23323</div>
            <div>长纤: 42332</div>
            <div>机械浆: 432</div>
            <div>总计: 66087</div>
          </div>
        </div>
        <div class="InvCurrTons-left-box2-content">
          <div
            v-for="(item, index) in OldDump.items"
            :key="index"
            class="jdcBox"
          >
            <!-- kd: item.type === 0, -->
            <div
              class="kd"
              :class="{
                bd: item.type === 1,
                md: item.type === 2,
                bd2: item.type === 3
              }"
            >
              {{ item.name }}
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="InvCurrTons-right">
      <div class="toTitle">
        <h3>
          一万方堆场
        </h3>
        <div class="fTitle">
          <div>短纤: 23323</div>
          <div>长纤: 42332</div>
          <div>机械浆: 432</div>
          <div>总计: 66087</div>
        </div>
      </div>
      <div class="InvCurrTons-right-content">
        <div class="box"></div>
        <div class="grid1">
          <div class="grid1-l">
            <div
              v-for="(item, index) in tenKHeapData.items"
              :key="index"
              class="grid1-l-piece"
              :class="'grid1L' + (index + 1)"
            >
              <div
                class="kd"
                :class="{
                  bd: item.type === 1,
                  md: item.type === 2,
                  bd2: item.type === 3
                }"
              >
                {{ item.name }}
              </div>
            </div>
          </div>
          <div style="display: flex;align-items: flex-end;">
            <div
              class="vertical-dashed-line2"
              style="height: 42%;background-size: 2rem 1.4rem;"
            ></div>
            <div
              class="vertical-dashed-line2"
              style="height: 42%;background-size: 2rem 1.4rem;"
            ></div>
          </div>
          <div class="grid1-c">
            <div
              v-for="(item, index) in tenKHeapData.items2"
              :key="index"
              :class="'grid1C' + (index + 1)"
            >
              <div
                class="kd"
                :class="{
                  bd: item.type === 1,
                  md: item.type === 2,
                  bd2: item.type === 3
                }"
              >
                {{ item.name }}
              </div>
            </div>
          </div>
          <div class="vertical-dashed-line"></div>
          <div class="vertical-dashed-line2"></div>
          <div class="grid1-r">
            <div
              v-for="(item, index) in tenKHeapData.items3"
              :key="index"
              class="grid1-r-piece"
            >
              <div
                class="kd"
                :class="{
                  bd: item.type === 1,
                  md: item.type === 2,
                  bd2: item.type === 3
                }"
              >
                {{ item.name }}
              </div>
            </div>
          </div>
        </div>
        <div class="box" style="position: relative;">
          <div
            class="vertical-dashed-line3"
            style="width: 20%;top: 0;left: 1rem;"
          ></div>
          <div
            class="vertical-dashed-line3"
            style="width: 44%;top: 0;left: 8.8rem;"
          ></div>
          <div
            class="vertical-dashed-line3"
            style="width: 22%;top: 0;left: 24.4rem;"
          ></div>

          <div
            class="vertical-dashed-line3"
            style="width: 20%;top: 100%;left: 1rem;"
          ></div>
          <div
            class="vertical-dashed-line3"
            style="width: 44%;top: 100%;left: 8.8rem;"
          ></div>
          <div
            class="vertical-dashed-line3"
            style="width: 22%;top: 100%;left: 24.4rem;"
          ></div>
        </div>
        <div class="grid2">
          <div class="grid2-l">
            <div
              v-for="(item, index) in tenKHeapData.items4"
              :key="index"
              class="grid1-r-piece"
            >
              <div
                class="kd"
                :class="{
                  bd: item.type === 1,
                  md: item.type === 2,
                  bd2: item.type === 3
                }"
              >
                {{ item.name }}
              </div>
            </div>
          </div>
          <div class="vertical-dashed-line"></div>
          <div class="vertical-dashed-line2"></div>
          <div class="grid2-c">
            <div
              v-for="(item, index) in tenKHeapData.items5"
              :key="index"
              class="grid1-r-piece"
            >
              <div
                class="kd"
                :class="{
                  bd: item.type === 1,
                  md: item.type === 2,
                  bd2: item.type === 3
                }"
              >
                {{ item.name }}
              </div>
            </div>
          </div>
          <div class="vertical-dashed-line"></div>
          <div class="vertical-dashed-line2"></div>
          <div class="grid2-l">
            <div
              v-for="(item, index) in tenKHeapData.items6"
              :key="index"
              class="grid1-r-piece"
            >
              <div
                class="kd"
                :class="{
                  bd: item.type === 1,
                  md: item.type === 2,
                  bd2: item.type === 3
                }"
              >
                {{ item.name }}
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  components: {},
  data() {
    return {
      PulpData: {
        items: [
          { name: 111, type: 2 },
          { name: 110 },
          { name: 109 },
          { name: 108, type: 2 },
          { name: 107, type: 1 },
          { name: 106, type: 1 },
          { name: 105, type: 0 },
          { name: 104, type: 1 },
          { name: 103, type: 2 },
          { name: 102 },
          { name: 101, type: 2 }
        ],
        items1: [{ name: "2-3", type: 2 }],
        items2: [
          { name: 201, type: 2 },
          { name: 112, type: 2 },
          { name: 113, type: 1 },
          { name: 202, type: 1 },
          { name: 114 },
          { name: 203 },
          { name: "1-1", type: 2 },
          { name: "1-2" },
          { name: 204, type: 1 },
          { name: "1-3", type: 3 }
        ],
        items3: [
          { name: 205, type: 1 },
          { name: 206, type: 2 },
          { name: "2-1", type: 3 },
          { name: "2-2", type: 3 }
        ],
        items4: [
          { name: 301, type: 0 },
          { name: 302, type: 1 },
          { name: "3-1", type: 2 },
          { name: "3-2", type: 3 },
          { name: "3-3", type: 2 }
        ]
      },
      OldDump: {
        items: [
          { name: 601, type: 2 },
          { name: 602, type: 3 },
          { name: 603, type: 0 },
          { name: 604, type: 2 },
          { name: 605, type: 0 }
        ]
      },
      tenKHeapData: {
        items: [
          { name: 718, type: 2 },
          { name: 719, type: 1 },
          { name: 720, type: 0 }
        ],
        items2: [
          { name: 715, type: 2 },
          { name: 716, type: 2 },
          { name: 717, type: 1 },
          { name: 708, type: 2 },
          { name: 709, type: 0 },
          { name: 710, type: 1 },
          { name: 711, type: 1 },
          { name: 712, type: 1 },
          { name: 713, type: 2 },
          { name: 714, type: 2 }
        ],
        items3: [
          { name: 707, type: 0 },
          { name: 706, type: 2 },
          { name: 705, type: 1 },
          { name: 704, type: 2 },
          { name: 703, type: 1 },
          { name: 702, type: 2 },
          { name: 701, type: 1 }
        ],
        items4: [
          { name: 816, type: 2 },
          { name: 817, type: 2 },
          { name: 818, type: 1 },
          { name: 819, type: 1 },
          { name: 820, type: 1 }
        ],

        items5: [
          { name: 811, type: 2 },
          { name: 806, type: 2 },
          { name: 812, type: 5 },
          { name: 807, type: 1 },
          { name: 813, type: 1 },
          { name: 808, type: 0 },
          { name: 814, type: 2 },
          { name: 809, type: 2 },
          { name: 815, type: 1 },
          { name: 810, type: 0 }
        ],
        items6: [
          { name: 801, type: 2 },
          { name: 802, type: 1 },
          { name: 803, type: 2 },
          { name: 804, type: 0 },
          { name: 805, type: 0 }
        ]
      }

      //type说明: 空堆0 横半堆1 满堆2 竖半堆3
    };
  },
  //监听属性 类似于data概念
  computed: {},
  //监控data中的数据变化
  watch: {},
  //方法集合
  methods: {},
  //生命周期 - 创建完成(可以访问当前this实例)
  created() {},
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {},
  beforeCreate() {}, //生命周期 - 创建之前
  beforeMount() {}, //生命周期 - 挂载之前
  beforeUpdate() {}, //生命周期 - 更新之前
  updated() {}, //生命周期 - 更新之后
  beforeDestroy() {}, //生命周期 - 销毁之前
  destroyed() {}, //生命周期 - 销毁完成
  activated() {} //如果页面有keep-alive缓存功能,这个函数会触发
};
</script>

<style lang="less" scoped>
//@import url(); 引入公共css类
.kd {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  border: 1px solid #065178;
}
.bd {
  border: 1px solid #065178;
  background: linear-gradient(to right, #0073a7 70%, #e9e9e900 0);
}
.md {
  border: 0px solid;
  background: #0073a7;
}
.bd2 {
  border: 1px solid #065178;
  background: linear-gradient(to bottom, #0073a7 70%, #e9e9e900 0);
}

.toTitle {
  height: 10%;
  text-align: center;
  color: #0184cf;
  .fTitle {
    color: #71c5e5;
    display: flex;
    justify-content: center;
    div {
      margin-right: 0.6rem;
    }
  }
}
.InvCurrTons {
  display: flex;
  height: 100%;
  &-left {
    width: 60%;
    margin: 1rem 0rem 2rem;

    &-box1 {
      height: 70%;
      background: url("../../img/New/cardB.svg") no-repeat;
      background-size: 100% 100%;
      background-position: center top;
      margin: 0 0.5rem;
      padding: 0.5rem;

      &-content {
        display: flex;
        flex-direction: row;
        position: relative;
        height: 90%;
        padding: 0.4rem 0;
        .parent {
          text-align: center;
          display: flex;
          flex-direction: column;
          justify-content: space-between;
        }

        .parent-right {
          .parent-right-item1 {
            height: 50%;
          }
          .parent-right-item2 {
            position: relative;
            height: 50%;
            display: flex;
          }
        }
      }
    }
    &-box2 {
      height: 30%;
      background: url("../../img/New/cardB.svg") no-repeat;
      background-size: 100% 100%;
      background-position: center top;
      margin: 0.5rem 0.5rem 0;
      &-content {
        display: flex;
        flex-direction: row;
        position: relative;
        height: 80%;
        padding: 0.4rem 0;
        justify-content: center;
        .jdcBox {
          padding: 1rem;
          width: 7rem;
        }
      }
    }
  }
  &-right {
    width: 45%;
    background: url("../../img/New/cardB.svg") no-repeat;
    background-size: 100% 100%;
    background-position: center top;
    margin: 1rem 0rem 1.5rem;
    &-content {
      height: 90%;
      position: relative;
      .box {
        height: 10%;
      }
      .grid1 {
        height: 45%;
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        padding: 0 1rem;
        &-l {
          width: 23%;
          padding-right: 0.3rem;
          display: grid;
          grid-template-columns: 1fr;
          grid-template-rows: repeat(7, 1fr);
          grid-column-gap: 0;
          grid-row-gap: 0.4rem;
          padding-bottom: 0.3rem;
        }
        &-c {
          height: 100%;
          width: 46%;
          display: grid;
          grid-template-columns: repeat(2, 1fr);
          grid-template-rows: repeat(7, 1fr);
          grid-column-gap: 0.4rem;
          grid-row-gap: 4px;
          padding-bottom: 0.3rem;
        }
        &-r {
          height: 100%;
          width: 23%;
          display: grid;
          grid-template-columns: 1fr;
          grid-template-rows: repeat(7, 1fr);
          grid-column-gap: 0px;
          grid-row-gap: 4px;
          padding-bottom: 0.3rem;
        }
      }
      .grid2 {
        height: 35%;
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        padding: 0 1rem;
        &-l {
          height: 100%;
          width: 23%;
          display: grid;
          grid-template-columns: 1fr;
          grid-template-rows: repeat(5, 1fr);
          grid-column-gap: 0px;
          grid-row-gap: 4px;
          padding: 0.3rem 0 0.5rem;
        }
        &-c {
          width: 46%;
          display: grid;
          grid-template-columns: repeat(2, 1fr);
          grid-template-rows: repeat(5, 1fr);
          grid-column-gap: 0.4rem;
          grid-row-gap: 4px;
          padding: 0.3rem 0 0.5rem;
        }
      }
    }
  }
}

.vertical-dashed-line {
  width: 2px;
  height: 97%;

  background-image: linear-gradient(
    to bottom,
    rgba(78, 83, 88, 1) 0%,
    rgba(78, 83, 88, 1) 50%,
    transparent 80%
  );
  background-size: 2rem 2rem; //第一个值(20px)越大线条越长间隙越大
  margin: 0 0.5rem 0.5rem;
}
.vertical-dashed-line2 {
  width: 2px;
  height: 97%;
  background-image: linear-gradient(
    to bottom,
    rgba(40, 51, 63, 1) 0%,
    rgba(40, 51, 63, 1) 50%,
    transparent 80%
  );
  background-size: 2rem 2rem; //第一个值(20px)越大线条越长间隙越大
  margin-right: 0.5rem;
}
.vertical-dashed-line3 {
  width: 35%;
  height: 2px;
  background-image: linear-gradient(
    to right,
    rgba(78, 83, 88, 1) 0%,
    rgba(78, 83, 88, 1) 50%,
    transparent 80%
  );
  background-size: 2rem 2rem; //第一个值(20px)越大线条越长间隙越大
  margin-right: 0.5rem;
  position: absolute;
  top: -8px;
  left: 11%;
}
.vertical-dashed-line4 {
  width: 3px;
  height: 97%;
  background-image: linear-gradient(to bottom, rgba(78, 83, 88, 1));
  background-size: 2rem 2rem; //第一个值(20px)越大线条越长间隙越大
  margin-right: 0.2rem;
}
</style>

<style>
.parent2 {
  width: 100%;
  text-align: center;
  display: grid;
  grid-template-columns: repeat(2, 1.3fr) 0.8fr repeat(2, 1fr);
  grid-template-rows: repeat(16, 1fr);
  grid-column-gap: 2px;
  grid-row-gap: 2px;
}
.LBDiv1 {
  grid-area: 13/4/17/6;
}
.LBDiv2 {
  grid-area: 14 / 2 / 17 / 4;
}
.LBDiv3 {
  grid-area: 11 / 2 / 14 / 4;
}
.LBDiv4 {
  grid-area: 9 / 4 / 13 / 6;
}
.LBDiv5 {
  grid-area: 8 / 2 / 11 / 4;
}
.LBDiv6 {
  grid-area: 5 / 4 / 9 / 6;
}
.LBDiv7 {
  grid-area: 1 / 2 / 8 / 3;
}
.LBDiv8 {
  grid-area: 1 / 3 / 8 / 4;
}
.LBDiv9 {
  grid-area: 1 / 4 / 5 / 6;
}
.LBDiv10 {
  grid-area: 1 / 1 / 14 / 2;
  margin-right: 0.5rem;
}

.parent3 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-rows: repeat(5, 1fr);
  grid-column-gap: 10px;
  grid-row-gap: 5px;
}

.CBDiv1 {
  grid-area: 5 / 1 / 6 / 5;
}
.CBDiv2 {
  grid-area: 4 / 1 / 5 / 5;
}
.CBDiv3 {
  grid-area: 1 / 1 / 4 / 3;
}
.CBDiv4 {
  grid-area: 1 / 3 / 4 / 5;
}

.parent4 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(5, 1fr);
  grid-column-gap: 10px;
  grid-row-gap: 5px;
}

.RBDiv1 {
  grid-area: 5 / 1 / 6 / 4;
}
.RBDiv2 {
  grid-area: 4 / 1 / 5 / 4;
}
.RBDiv3 {
  grid-area: 1 / 1 / 4 / 2;
}
.RBDiv4 {
  grid-area: 1 / 2 / 4 / 3;
}
.RBDiv5 {
  grid-area: 1 / 3 / 4 / 4;
}

.parent5 {
  height: 91%;
  display: grid;
  grid-template-columns: repeat(6, 1fr) 0.9fr repeat(3, 1fr);
  grid-template-rows: repeat(5, 1fr);
  grid-column-gap: 0px;
  grid-row-gap: 0px;
}

.box1 {
  grid-area: 3 / 7 / 6 / 8;
}

.grid1L1 {
  grid-area: 7 / 1 / 8 / 2;
}
.grid1L2 {
  grid-area: 6 / 1 / 7 / 2;
}
.grid1L3 {
  grid-area: 5 / 1 / 6 / 2;
}

.grid1C1 {
  grid-area: 7 / 1 / 8 / 2;
}
.grid1C2 {
  grid-area: 6 / 1 / 7 / 2;
}
.grid1C3 {
  grid-area: 5 / 1 / 6 / 2;
}
.grid1C4 {
  grid-area: 7 / 2 / 8 / 3;
}
.grid1C5 {
  grid-area: 6 / 2 / 7 / 3;
}
.grid1C6 {
  grid-area: 5 / 2 / 6 / 3;
}
.grid1C7 {
  grid-area: 4 / 2 / 5 / 3;
}
.grid1C8 {
  grid-area: 3 / 2 / 4 / 3;
}
.grid1C9 {
  grid-area: 2 / 2 / 3 / 3;
}
.grid1C10 {
  grid-area: 1 / 2 / 2 / 3;
}
</style>
相关推荐
2401_882727579 分钟前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
会发光的猪。43 分钟前
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
前端·javascript·vue.js
天下代码客1 小时前
【vue】vue中.sync修饰符如何使用--详细代码对比
前端·javascript·vue.js
猫爪笔记1 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
前端李易安1 小时前
Webpack 热更新(HMR)详解:原理与实现
前端·webpack·node.js
红绿鲤鱼1 小时前
React-自定义Hook与逻辑共享
前端·react.js·前端框架
Domain-zhuo2 小时前
什么是JavaScript原型链?
开发语言·前端·javascript·jvm·ecmascript·原型模式
小丁爱养花2 小时前
前端三剑客(三):JavaScript
开发语言·前端·javascript
ZwaterZ2 小时前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue
西凉河的葛三叔2 小时前
vue3+elementui-plus el-dialog全局配置点击空白处不关闭弹窗
前端·vue3·elementui-plus