OrchardCore Admin Edit Views
SkillFiles & storageLets your agent create and update OrchardCore admin edit views with the correct layout and styling classes.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the OrchardCore Admin Edit Views skill
About this capability
Creates and updates OrchardCore admin edit views using the ocat-* CSS class conventions. Use when creating or modifying Edit view files (*.Edit.cshtml, *.Fields.Edit.cshtml) to ensure correct layout and styling.
What this skill tells your AI
The instructions your AI receives, as published by orchardcms/orchardcore in .agents/skills/orchardcore-admin-edit-views/SKILL.md and read by ahel’s review.
This skill guides you through creating and updating admin edit view files using the ocat-* CSS class conventions required by the OrchardCore admin theme.
The ocat-* CSS Class System
The ocat-* CSS classes provide a standardized two-column layout for admin form fields. Every form field uses a consistent structure:
- Left column – The field label (
ocat-label) - Right column – The input, validation message, and hint (
ocat-end)
Core Classes
| Class | Element | Purpose |
|---|---|---|
ocat-wrapper | <div> | Outer container for a form field row (replaces mb-3/form-group) |
ocat-label | <label> | Styles the field label in the left column (replaces form-label) |
ocat-label-required | <label> | Add alongside ocat-label to mark a field as required |
ocat-end | <div> | Wraps the input, validation, and hint in the right column |
ocat-end-offset | <div> | Right-column content with no left label (e.g., checkboxes) |
ocat-limited-wrapper | <div> | Outer row for limited-width controls such as short text, numbers, selects, or small settings inputs |
ocat-limited | <div> | Constrains the control width inside ocat-limited-wrapper |
Choosing the Right Wrapper
Use this decision guide when creating or updating admin edit views:
| Scenario | Preferred Pattern |
|---|---|
| Standard full-width field row | ocat-wrapper + ocat-label + ocat-end |
| Standalone checkbox/toggle row with no left label | ocat-wrapper + ocat-end-offset |
| Standalone alert, notice, legend, or section headline in the form flow | ocat-wrapper + ocat-end-offset |
| Top-level limited-width field row (small text, number, select, token, path, port, etc.) | ocat-limited-wrapper + ocat-label + ocat-limited |
Content part / content field editor that already needs wrapper classes like field-wrapper-* or content-part-wrapper-* | Keep the outer ocat-wrapper, then place ocat-limited-wrapper inside ocat-end |
Wrapper Selection Rules
- Use
ocat-wrapperfor the default admin edit-view row structure. - Use
ocat-limited-wrapperwhen the field should stay visually narrow instead of stretching the full editor width. - If the outer row also needs Orchard-specific wrapper classes such as
field-wrapper-*orcontent-part-wrapper-*, do not replace that outer row withocat-limited-wrapper; keep the outerocat-wrapperand nestocat-limited-wrapperinsideocat-end. - Do not use legacy helpers or Bootstrap-only layout classes like
mb-3,form-group,form-label,@Orchard.GetWrapperClasses(), or@Orchard.GetLimitedWidthWrapperClasses(). - Alerts, notices,
<legend>, and section headings that are part of the edit form but have no left-column label should use their ownocat-wrapper+ocat-end-offsetrow. - A single row should use either
ocat-endorocat-end-offset, never both.
Standard Field Structure
<div class="ocat-wrapper" asp-validation-class-for="FieldName">
<label asp-for="FieldName" class="ocat-label">@T["Label Text"]</label>
<div class="ocat-end">
<input asp-for="FieldName" class="form-control" />
<span asp-validation-for="FieldName"></span>
<span class="hint">@T["Descriptive hint text."]</span>
</div>
</div>
Field Type Templates
Text Input
<div class="ocat-wrapper" asp-validation-class-for="FieldName">
<label asp-for="FieldName" class="ocat-label">@T["Label"]</label>
<div class="ocat-end">
<input asp-for="FieldName" class="form-control" />
<span asp-validation-for="FieldName"></span>
<span class="hint">@T["Hint text."]</span>
</div>
</div>
Required Text Input
<div class="ocat-wrapper" asp-validation-class-for="FieldName">
<label asp-for="FieldName" class="ocat-label ocat-label-required">@T["Label"]</label>
<div class="ocat-end">
<input asp-for="FieldName" class="form-control" />
<span asp-validation-for="FieldName"></span>
<span class="hint">@T["Hint text."]</span>
</div>
</div>
Code/Script Input
<div class="ocat-wrapper" asp-validation-class-for="Expression">
<label asp-for="Expression" class="ocat-label">@T["Expression"]</label>
<div class="ocat-end">
<input type="text" asp-for="Expression" class="form-control code" />
<span asp-validation-for="Expression"></span>
<span class="hint">@T["A JavaScript expression. Example: {0}", "input(\"Count\") > 0"]</span>
</div>
</div>
Textarea
<div class="ocat-wrapper" asp-validation-class-for="Content">
<label asp-for="Content" class="ocat-label">@T["Content"]</label>
<div class="ocat-end">
<textarea asp-for="Content" class="form-control" rows="5"></textarea>
<span asp-validation-for="Content"></span>
<span class="hint">@T["Hint text."]</span>
</div>
</div>
Select / Dropdown
<div class="ocat-wrapper" asp-validation-class-for="FieldName">
<label asp-for="FieldName" class="ocat-label">@T["Label"]</label>
<div class="ocat-end">
<select asp-for="FieldName" class="form-select">
<option value="Option1">@T["Option 1"]</option>
<option value="Option2">@T["Option 2"]</option>
</select>
<span asp-validation-for="FieldName"></span>
<span class="hint">@T["Hint text."]</span>
</div>
</div>
Checkbox (No Left Label)
Use ocat-end-offset when there is no label in the left column (e.g., for checkboxes):
<div class="ocat-wrapper" asp-validation-class-for="IsEnabled">
<div class="ocat-end-offset">
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="IsEnabled" />
<label asp-for="IsEnabled" class="form-check-label">@T["Enable feature"]</label>
</div>
<span class="hint dashed">@T["Check to enable this feature."]</span>
</div>
</div>
Headline / Alert Row (No Left Label)
Use this for standalone form guidance, warnings, legends, or section headers:
<div class="ocat-wrapper">
<div class="ocat-end-offset">
<h5>@T["Section heading"]</h5>
<div class="alert alert-warning" role="alert">
@T["Important information for editors."]
</div>
</div>
</div>
Input Group (with prefix/suffix)
<div class="ocat-wrapper" asp-validation-class-for="Url">
<label asp-for="Url" class="ocat-label">@T["URL"]</label>
<div class="ocat-end">
<div class="input-group">
<span class="input-group-text">https://</span>
<input asp-for="Url" type="text" class="form-control" />
</div>
<span asp-validation-for="Url"></span>
<span class="hint">@T["The full URL."]</span>
</div>
</div>
Limited-Width Field Row
Use this when the whole row is a compact admin field, such as a page size, port, callback path, or select list:
<div class="ocat-limited-wrapper" asp-validation-class-for="PageSize">
<label asp-for="PageSize" class="ocat-label">@T["Page size"]</label>
<div class="ocat-limited">
<input asp-for="PageSize" type="number" class="form-control" />
<span asp-validation-for="PageSize"></span>
<span class="hint">@T["The default page size."]</span>
</div>
</div>
Limited-Width Select Row
<div class="ocat-limited-wrapper" asp-validation-class-for="TimeZone">
<label asp-for="TimeZone" class="ocat-label">@T["Default Time Zone"]</label>
<div class="ocat-limited">
<select asp-for="TimeZone" class="form-select">
<option value="">@T["Local to server"]</option>
</select>
<span asp-validation-for="TimeZone"></span>
<span class="hint">@T["Determines the default time zone."]</span>
</div>
</div>
Limited-Width Control Inside a Field Wrapper
Use this when the editor already needs a content field or content part wrapper on the outer row:
<div class="@($"ocat-wrapper field-wrapper field-wrapper-{Model.PartFieldDefinition.PartDefinition.Name.HtmlClassify()}-{Model.PartFieldDefinition.Name.HtmlClassify()}")">
<label asp-for="Value" class="ocat-label">@T["Value"]</label>
<div class="ocat-end">
<div class="ocat-limited-wrapper">
<div class="ocat-limited">
<input asp-for="Value" class="form-control" />
<span asp-validation-for="Value"></span>
</div>
</div>
<span class="hint">@T["Use a compact editor width while preserving the field wrapper row."]</span>
</div>
</div>
Conditional Fields with JavaScript/Liquid Syntax Selector
For fields that support both JavaScript and Liquid syntax, use the syntax selector pattern with toggling d-none:
<div class="ocat-wrapper" asp-validation-class-for="Syntax">
<label asp-for="Syntax" class="ocat-label">@T["Syntax"]</label>
<div class="ocat-end">
<select asp-for="Syntax" class="form-select">
<option value="@nameof(WorkflowScriptSyntax.JavaScript)">@T["JavaScript"]</option>
<option value="@nameof(WorkflowScriptSyntax.Liquid)">@T["Liquid"]</option>
</select>
<span asp-validation-for="Syntax"></span>
</div>
</div>
<div id="@(Html.IdFor(m => m.Expression))_group" class="ocat-wrapper @(Model.Syntax == WorkflowScriptSyntax.JavaScript ? "" : "d-none")" asp-validation-class-for="Expression">
<label asp-for="Expression" class="ocat-label">@T["Expression"]</label>
<div class="ocat-end">
<input type="text" asp-for="Expression" class="form-control code" />
<span asp-validation-for="Expression"></span>
<span class="hint">@T["A JavaScript expression. Example: {0}", "input(\"Count\") > 0"]</span>
</div>
</div>
<div id="@(Html.IdFor(m => m.LiquidExpression))_group" class="ocat-wrapper @(Model.Syntax == WorkflowScriptSyntax.Liquid ? "" : "d-none")" asp-validation-class-for="LiquidExpression">
<label asp-for="LiquidExpression" class="ocat-label">@T["Expression"]</label>
<div class="ocat-end">
<input type="text" asp-for="LiquidExpression" class="form-control code" />
<span asp-validation-for="LiquidExpression"></span>
<span class="hint">@T["A Liquid expression. Example: {0}", "{{ Workflow.Properties.Value }}"]</span>
</div>
</div>
<script at="Foot">
document.addEventListener('DOMContentLoaded', () => {
const syntaxElement = document.getElementById('@Html.IdFor(m => m.Syntax)');
const javaScriptGroup = document.getElementById('@($"{Html.IdFor(m => m.Expression)}_group")');
const liquidGroup = document.getElementById('@($"{Html.IdFor(m => m.LiquidExpression)}_group")');
const toggleEditors = () => {
const useLiquid = syntaxElement.value === '@nameof(WorkflowScriptSyntax.Liquid)';
javaScriptGroup.classList.toggle('d-none', useLiquid);
liquidGroup.classList.toggle('d-none', !useLiquid);
};
syntaxElement.addEventListener('change', toggleEditors);
toggleEditors();
});
</script>
Display-Only Fields (Read-Only)
For displaying information without a form input:
<div class="ocat-wrapper">
<label class="ocat-label">@T["Field Label"]</label>
<div class="ocat-end">
<span>@Model.FieldValue</span>
</div>
</div>
Migration: Old to New Classes
When updating existing views, apply these replacements:
| Old Pattern | New Pattern |
|---|---|
<div class="mb-3" ...> | <div class="ocat-wrapper" ...> |
<div class="form-group" ...> | <div class="ocat-wrapper" ...> |
<label ... class="form-label"> | <label ... class="ocat-label"> |
| Input/select/textarea directly inside wrapper | Wrap in <div class="ocat-end"> |
| Checkbox with label inside wrapper | Wrap in <div class="ocat-end-offset"><div class="form-check"> |
| Standalone headline or alert directly in the form flow | Wrap in <div class="ocat-wrapper"><div class="ocat-end-offset">...</div></div> |
Mixing ocat-end and ocat-end-offset in one row | Choose one wrapper based on whether there is a left-column label |
@Orchard.GetLimitedWidthWrapperClasses() | ocat-limited-wrapper |
@Orchard.GetLimitedWidthClasses() | ocat-limited |
Limited-width content field row using field-wrapper-* | Keep outer ocat-wrapper field-wrapper-*, nest ocat-limited-wrapper inside ocat-end |
Complete Edit View Example
@model MyModuleViewModel
<div class="ocat-wrapper" asp-validation-class-for="Title">
<label asp-for="Title" class="ocat-label ocat-label-required">@T["Title"]</label>
<div class="ocat-end">
<input asp-for="Title" class="form-control" autofocus="autofocus" />
<span asp-validation-for="Title"></span>
<span class="hint">@T["The title of the item."]</span>
</div>
</div>
<div class="ocat-wrapper" asp-validation-class-for="Description">
<label asp-for="Description" class="ocat-label">@T["Description"]</label>
<div class="ocat-end">
<textarea asp-for="Description" class="form-control" rows="4"></textarea>
<span asp-validation-for="Description"></span>
<span class="hint">@T["A brief description."]</span>
</div>
</div>
<div class="ocat-wrapper" asp-validation-class-for="Category">
<label asp-for="Category" class="ocat-label">@T["Category"]</label>
<div class="ocat-end">
<select asp-for="Category" class="form-select">
<option value="">@T["-- Select --"]</option>
<option value="A">@T["Category A"]</option>
<option value="B">@T["Category B"]</option>
</select>
<span asp-validation-for="Category"></span>
</div>
</div>
<div class="ocat-wrapper" asp-validation-class-for="IsPublished">
<div class="ocat-end-offset">
<div class="form-check">
<input type="checkbox" class="form-check-input" asp-for="IsPublished" />
<label asp-for="IsPublished" class="form-check-label">@T["Published"]</label>
</div>
<span class="hint dashed">@T["Check to publish this item."]</span>
</div>
</div>
References
references/ocat-classes.md- Detailed class reference with CSS context- Admin theme source:
src/OrchardCore.Themes/TheAdmin/ - Example usage:
src/OrchardCore.Modules/OrchardCore.Workflows/Views/Items/ - AGENTS.md (repo root) - Coding conventions and build commands
Signals
- GitHub stars
- 8k
- Forks
- 3k
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
orchardcore-admin-edit-views- Source
- github.com/orchardcms/orchardcore