再論 Blaozr CSS 隔離與 ::deep 虛擬元素

CSS隔離,CSS isolation, ::deep,

引言

在 Blazor Component 應用開發中經常會先引用一套 CSS Framework,如:MudBlazor, MatBlazor 等等來做為整個 Web UI 的 CSS 基底,之後的客製元件再以此為基礎疊床架屋。這時 Blaozr Component 的 CSS 影響力是不能影響自身以外的。

然若配合 ::deep 虛擬元素就能解決此問題。

參考文件

::deep 虛擬元素

這個 ::deep 虛擬元素不是所有的CSS 預處理器都支援的樣子。不過主流的都有支援才會不時的看到它。其指令形式可能不同,已知有另一套的語形是 /deep/

::deep 只會影響由它開始的子孫層元素,所以::deep放的位置很重要。若它的子孫層是基底的 CSS Framework 那也會被影響到,不過只在 ::deep 之下被影響不是全面覆寫 CSS。

使用::deep還是有隔離效果的,應用在以 CSS Framework 為基底疊床架屋的客製化元件是很好用的。

關鍵原碼

一個疊床架屋的客製 Blaozr 元件。此例中 第三方元件 PortRenderer 是客製化標的,它的 CSS class 名為'.port' 。

FooCustomWidget.razor
@using Blazor.Diagrams.Components.Renderers

<div class="custom-node @(Node.Selected ? " selected" : "")">
  <MudCard>
    <MudCardContent>
      <MudText Typo=Typo.h6>Bot Answer</MudText>
      <MudInput @bind-Value=@(Node.Answer) Placeholder="Answer" Variant=Variant.Outlined Margin=Margin.Dense />
    </MudCardContent>
  </MudCard>

  @foreach (var port in Node.Ports)
  {
    <PortRenderer Port=@port></PortRenderer>
    @* 此第三方元件的 CSS class 名為'.port',是客製化標的。 *@
  }
</div>

@code {
  [Parameter] public BotAnswerNode Node { get; set; }
}

關鍵原碼-未使用::deep

FooCustomWidget.razor.css
.custom-node.selected,
.custom-node.selected .port {
  border: 1px solid #6e9fd4;
}

.custom-node:hover .port {
  visibility: visible;
}

.custom-node .port {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: #f5f5f5;
  border: 1px solid #d4d4d4;
  cursor: pointer;
  visibility: hidden;
}

  .custom-node .port:hover,
  .custom-node .port.has-links {
    visibility: visible;
    background-color: black;
  }

  .custom-node .port.bottom {
    position: absolute;
    bottom: -10px;
    left: calc(50% - 10px);
  }

  .custom-node .port.top {
    position: absolute;
    top: -10px;
    left: calc(50% - 10px);
  }

產生的 CSS 如下,可以看到隔離的屬性在最終端的元素,此例為 [b-houkg0n40n],影響範圍只有自己。這無法影響目標 PortRenderer 的 CSS 呈現。

/* 隔離的屬性在最終端的 element,此例為 [b-houkg0n40n],也就是影響範圍只有自己。 */

.custom-node.selected[b-houkg0n40n],
.custom-node.selected .port[b-houkg0n40n] {
  border: 1px solid #6e9fd4;
}

.custom-node:hover .port[b-houkg0n40n] {
  visibility: visible;
}

.custom-node .port[b-houkg0n40n] {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: #f5f5f5;
  border: 1px solid #d4d4d4;
  cursor: pointer;
  visibility: hidden;
}

  .custom-node .port:hover[b-houkg0n40n],
  .custom-node .port.has-links[b-houkg0n40n] {
    visibility: visible;
    background-color: black;
  }

  .custom-node .port.bottom[b-houkg0n40n] {
    position: absolute;
    bottom: -10px;
    left: calc(50% - 10px);
  }

  .custom-node .port.top[b-houkg0n40n] {
    position: absolute;
    top: -10px;
    left: calc(50% - 10px);
  }

關鍵原碼-使用::deep

FooCustomWidget.razor.css
.custom-node.selected,
.custom-node.selected ::deep .port {
  border: 1px solid #6e9fd4;
}

.custom-node:hover ::deep .port {
  visibility: visible;
}

.custom-node ::deep .port {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: #f5f5f5;
  border: 1px solid #d4d4d4;
  cursor: pointer;
  visibility: hidden;
}

  .custom-node ::deep .port:hover,
  .custom-node ::deep .port.has-links {
    visibility: visible;
    background-color: black;
  }

  .custom-node ::deep .port.bottom {
    position: absolute;
    bottom: -10px;
    left: calc(50% - 10px);
  }

  .custom-node ::deep .port.top {
    position: absolute;
    top: -10px;
    left: calc(50% - 10px);
  }

產生的 CSS 如下,可以看到隔離的屬性與 ::deep 放置位罝一樣。影響範圍包含其子孫層,也就是會影響第三方元件PortRenderer 的 CSS 呈現,它的 CSS class 名為'.port' 。

/* 隔離的屬性與::deep放置位置一樣。此例為 [b-houkg0n40n],將影響其子孫層元素。 */

.custom-node.selected[b-houkg0n40n],
.custom-node.selected[b-houkg0n40n] .port {
  border: 1px solid #6e9fd4;
}

.custom-node:hover[b-houkg0n40n] .port {
  visibility: visible;
}

.custom-node[b-houkg0n40n] .port {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: #f5f5f5;
  border: 1px solid #d4d4d4;
  cursor: pointer;
  visibility: hidden;
}

  .custom-node[b-houkg0n40n] .port:hover,
  .custom-node[b-houkg0n40n] .port.has-links {
    visibility: visible;
    background-color: black;
  }

  .custom-node[b-houkg0n40n] .port.bottom {
    position: absolute;
    bottom: -10px;
    left: calc(50% - 10px);
  }

  .custom-node[b-houkg0n40n] .port.top {
    position: absolute;
    top: -10px;
    left: calc(50% - 10px);
  }

(EOF)

Last updated