拡張スタイルの仕組み
拡張スタイルは、プリザンター全体(または条件で絞った範囲)に適用できる CSS です。管理画面の「スタイル」がサイト単位の設定であるのに対し、拡張スタイルはシステム管理者が設定するグローバルなカスタマイズ手段です。このページでは、拡張スタイルがどう読み込まれ、HTML のどこに挿入され、どんな条件で絞り込まれるかを整理します。
管理画面のスタイルとの違い
| 拡張スタイル | 管理画面のスタイル | |
|---|---|---|
| 設定方法 | ファイル配置、または DB の Extensions テーブル | 管理画面から編集 |
| 適用範囲 | システム全体(フィルタリングで絞り込み可) | サイト単位 |
| HTML への挿入形式 | <link rel="stylesheet" href="...">(外部ファイル) | <style>(インライン) |
| 反映タイミング | ファイル・DB とも再起動またはパラメータリロードが必要 | 保存後すぐ反映 |
| サイトエディタ表示中 | 適用される | 適用されない |
設定方法
ファイルで設定する
App_Data/Parameters/ExtendedStyles/ に .css ファイルを置きます。
App_Data/
└── Parameters/
└── ExtendedStyles/
├── 01_base.css ← ファイル名順で読み込まれる
├── 02_custom.css
└── theme/
└── theme.css ← サブディレクトリも再帰的に読み込まれるファイルはファイル名のアルファベット順に処理されます。サブディレクトリも再帰的に走査されるので、テーマや機能ごとにディレクトリを分けて管理できます。
WARNING
ファイルはアプリ起動時(初期化処理)に読み込まれ、メモリに保持されます。ファイルを追加・変更・削除したら、プリザンターを再起動するか、特権ユーザでパラメータリロード(/admins/reloadparameters)を実行するまで反映されません。 確認したソースでは、パラメータリロードでも ExtendedStyles を読み直します(Initializer.cs)。
Extensions テーブルで設定する 1.4.7.0 以降
1.4.7.0 以降は、DB の Extensions テーブルで管理することもできます。ExtensionType に "Style"、Body に CSS 本文、ExtensionSettings に絞り込み条件を JSON で書きます。
確認したソースでは、Extensions テーブルの内容も起動時とパラメータリロードのときにだけ読み込まれ、メモリ上の Parameters.ExtendedStyles に追加されます(ExtensionInitializer.cs、ParametersInitializer.cs)。DB を書き換えただけでは反映されないため、再起動またはパラメータリロードが必要です。DB の拡張スタイルはファイルの拡張スタイルの後ろに、ExtensionName の順で並びます。
{
"SiteIdList": [123, 456],
"Controllers": ["items"],
"Actions": ["index"]
}読み込みの仕組み
起動時の初期化(ファイルの場合)
アプリ起動時に Implem.DefinitionAccessor.Initializer が App_Data/Parameters/ExtendedStyles/ を走査し、すべての .css ファイルをメモリに読み込んで Parameters.ExtendedStyles リストに保持します。
private static List<ExtendedStyle> ExtendedStyles(
string path = null, List<ExtendedStyle> list = null)
{
list = list ?? new List<ExtendedStyle>();
path = path ?? Path.Combine(ParametersPath, "ExtendedStyles");
var files = new DirectoryInfo(path)
.GetFiles("*.css")
.OrderBy(file => file.Name); // ファイル名順
foreach (var file in files)
{
var style = Files.Read(file.FullName);
if (style != null)
{
list.Add(new ExtendedStyle()
{
Name = file.Name,
Path = file.FullName,
Style = style
});
}
}
// サブディレクトリも再帰処理
foreach (var dir in new DirectoryInfo(path).GetDirectories()
.OrderBy(dir => dir.Name))
{
list = ExtendedStyles(dir.FullName, list);
}
return list;
}/resources/styles エンドポイント
拡張スタイルは HTML にインラインで埋め込まれるのではなく、/resources/styles エンドポイントを参照する <link rel="stylesheet"> タグとして出力されます。エンドポイントはクエリ文字列の site-id・id・controller・action とログインユーザの組織・グループ・ユーザ ID をもとに、該当する拡張スタイルを結合して返します。
public static ContentResultInheritance Get(Context context)
{
var siteId = context.QueryStrings.Long("site-id");
var id = context.QueryStrings.Long("id");
var controller = context.QueryStrings.Data("controller");
var action = context.QueryStrings.Data("action");
// ...
return new ContentResultInheritance
{
ContentType = "text/css",
Content = HtmlStyles.ExtendedStyles(
context: context,
deptId: context.DeptId,
groups: context.Groups,
userId: context.UserId,
siteTop: siteTop,
siteId: siteId,
id: id,
controller: controller,
action: action)
};
}このエンドポイントには [ResponseCache(Duration = int.MaxValue)] が付いており、ブラウザによる長期キャッシュが有効です。
図を読み込み中…
フィルタリングの仕組み
拡張スタイルは、拡張スクリプトと同じ ExtensionWhere<T> メソッドで絞り込まれます。
| 条件リストの状態 | 評価結果 |
|---|---|
| リストが空(null を含む) | 常に一致(条件なし扱い) |
| 値が指定されている | 一致するものだけ適用(肯定指定) |
- を先頭に付けた値 | 一致するもの以外に適用(否定指定) |
指定できる条件は次のとおりです。
| プロパティ | 説明 |
|---|---|
DeptIdList | 組織 ID のリスト |
GroupIdList | グループ ID のリスト |
UserIdList | ユーザ ID のリスト |
SiteIdList | サイト ID のリスト |
IdList | レコード ID のリスト |
Controllers | コントローラ名("items"、"users" など) |
Actions | アクション名("index"、"edit"、"new" など) |
ファイルの拡張スタイルは絞り込めない
ファイルから読み込んだ ExtendedStyle は絞り込み用のプロパティがすべて空なので、条件なし扱いとなりすべてのページに適用されます。特定のページやサイトだけに適用したい場合は、CSS のセレクタで絞り込むか、Extensions テーブルを使ってください。
HTML への挿入位置
拡張スタイルは <head> 内に <link rel="stylesheet" href="/resources/styles?..."> として挿入されます。そのページの条件に合う拡張スタイルが 1 つもない(結合結果が空の)ときはタグ自体が出力されません(_using: !extendedStyles.IsNullOrEmpty()。HtmlStyles.cs)。サイトのトップ(一覧の最上位)では、テナントの管理画面で設定したトップのスタイル(TopStyle)も先頭に結合されます(L56-L58)。
public static HtmlBuilder ExtendedStyles(this HtmlBuilder hb, Context context)
{
var extendedStyles = ExtendedStyles(context: context);
return hb
.Link(
rel: "stylesheet",
href: $"resources/styles?v={extendedStyles.Sha512Cng()}"
+ $"&site-id={context.SiteId}"
+ $"&id={context.Id}"
+ $"&controller={context.Controller}"
+ $"&action={context.Action}",
_using: !extendedStyles.IsNullOrEmpty());
}<head> 内のスタイルシートと、<body> 内の管理画面のスタイルの並び順は次のとおりです(HtmlTemplates.cs、L136-L142)。
<head>
<!-- Normalize.css, jQuery-UI CSS, Material Symbolsなど標準CSS -->
<link rel="stylesheet" href="assets/plugins/Normalize.css">
<link rel="stylesheet" href="assets/themes/...">
<!-- プリザンター本体のスタイルシート (legacy.min.css または style.min.css) -->
<link rel="stylesheet" href="assets/css/legacy.min.css?v=...">
<!-- 外部リンクスタイル(管理画面の「ヘッドリンク」設定) -->
<link rel="stylesheet" href="...">
<!-- 拡張スタイル(ここ) -->
<link rel="stylesheet" href="/resources/styles?v=...">
<title>...</title>
</head>
<body>
<!-- メインコンテンツ -->
<!-- 管理画面のスタイル(<style>インラインタグ。body 内、スクリプトより前) -->
<style>/* 管理画面のスタイル(All) */</style>
<style>/* ページ固有の管理画面スタイル */</style>
...
</body>拡張スタイルはプリザンター本体のスタイルシートの後に入るため、標準スタイルを上書きする CSS を書きやすい位置です。管理画面のスタイル(<style> タグ)は <head> ではなく <body> 内に出力され、文書の中ではさらに後に来るので、セレクタと詳細度が同じ場合の優先順位は次のようになります。
図を読み込み中…
v パラメータとキャッシュバスティング
/resources/styles?v=... の v には、すべての拡張スタイルを結合した文字列の SHA512 ハッシュが入ります。スタイルの内容が変わるとハッシュも変わり、ブラウザのキャッシュが無効になります。逆に、内容が変わらない限りキャッシュは効き続けます。
管理画面のスタイルの場合
管理画面の「スタイル」(SiteSettings.Styles)はサイトごとに設定し、適用する画面(新規・編集・一覧・カレンダーなど)を選べます。出力は <style> のインラインタグです。
public string GetStyleBody(Context context, Func<Style, bool> peredicate)
{
return !IsSiteEditor(context: context)
? Styles?
.Where(style => style.Disabled != true && peredicate(style))
.Select(o => o.Body).Join("\n")
: null;
}INFO
管理画面でサイトを編集している間(サイトエディタを開いている状態)は、管理画面のスタイルは適用されません(IsSiteEditor が true のとき null を返します)。スタイルが管理画面の表示に影響しないようにするための制御です。拡張スタイルにはこの制限はありません。
運用上の注意点
- 変更は再起動またはパラメータリロードまで反映されません。 ファイルでも Extensions テーブルでも、読み込みは起動時とパラメータリロードのときだけです。
- ファイルの拡張スタイルは全ページに効きます。 範囲を絞るにはセレクタで絞り込むか、Extensions テーブルを使います。
- 詳細度が最優先です。 挿入順が効くのは詳細度が同じ場合だけです。意図どおり上書きできているか、ブラウザの開発者ツールで確認してください。
!importantは多用しないでください。 プリザンターの一部のスタイルには!importantが使われており、それを上書きするには同じく!importantが必要ですが、多用するとデバッグが難しくなります。できるだけセレクタの詳細度を上げる方法を優先します。- 広いセレクタは避けてください。
*やbodyなど広い範囲に効くセレクタは、他の機能や画面に意図しない影響を与えることがあります。影響範囲の狭い具体的なセレクタを使います。 - 開発中はキャッシュに注意してください。 内容が変わらない限りキャッシュが効き続けるため、開発・デバッグ中は
Ctrl + F5(ハードリロード)やブラウザのキャッシュ無効化設定を使って最新の状態を確認します。
まとめ
| 項目 | 内容 |
|---|---|
| ファイルの配置場所 | App_Data/Parameters/ExtendedStyles/*.css(サブディレクトリも再帰、ファイル名順) |
| 読み込みタイミング | ファイル・DB ともアプリ起動時とパラメータリロード時 |
| HTML への挿入形式 | <head> 内に <link rel="stylesheet" href="/resources/styles?v=..."> |
| 挿入位置 | プリザンター標準 CSS の後、管理画面のスタイルの前 |
| フィルタリング | 組織・グループ・ユーザ・サイト ID・レコード ID・コントローラ・アクション(DB で設定した場合のみ) |
| キャッシュ | 内容の SHA512 ハッシュで URL が変わり、キャッシュが無効になる |