"Every old system needs an Ark."
ARK is a modern UI framework for ABAP, extracted and refined from the battle-tested abapGit project. It enables developers to build rich, interactive HTML-based user interfaces within SAP GUI using familiar ABAP patterns.
⚠️ Experimental ProjectThis is an experimental project. APIs, class names, and behavior may change without notice between releases. While ARK is derived from abapGit's source code, it is an independent project — not officially endorsed by or maintained by the abapGit team or SAP. No warranty is provided.
- Do not use in production systems without thorough testing.
- Expect breaking changes as the framework evolves.
- Feedback, bug reports, and contributions are welcome, but treat current builds as alpha-quality.
- HTML Rendering Engine — Fluent API for building HTML with automatic indentation
- Page Navigation — automatic page stack: navigation pushes history,
go_back( )pops one level (restoring the previous page instance with its state). Every non-home page gets a uniform ← 返回 control automatically; the reserved actionark_backis intercepted by the framework. F3/Back goes one level up and exits only at the top level — no more jumping straight to exit - Event System — Sapevent-based communication between frontend and ABAP backend
- UI Components — Form, Table, Toolbar, and more ready-to-use components
- JS Library Registry —
zcl_ark_js_library=>register( iv_name iv_mime )plugs any uploaded JS (three.js, dayjs, ...) into every page;include_for( lo_widget )injects a widget's dependencies once per page - JS-ABAP Event Bridge —
zcl_ark_js_bridgeexposeswindow.arkEmit(action, params)so any component can fire sapevents without hand-rolled URL plumbing - Widget Plugin Interface — implement
zif_ark_js_widget(get_assets+render) and your class is a first-class page capability, same as the built-in table/JSON/chart components - Generic Data Rendering —
zcl_ark_html_table=>from_any_table( )turns any internal table into an HTML table via RTTI;zcl_ark_json_treerenders any JSON string as a collapsible tree — no per-cell code in either direction - ECharts Component — Declarative charting with
zcl_ark_echarts, mixable with any other HTML content; chart click events (drill-down) and GeoJSON maps (bundled China map) included - Text Templates —
zcl_ark_templatewith{{PLACEHOLDER}}syntax, loadable from the MIME repository - JSON Serialization —
zcl_ark_json=>to_json( )for any ABAP data object (sXML-based, zero dependencies) - Theme Support — Fiori Quartz design tokens via
zcl_ark_theme; override any token withset_token( ) - Declarative State Pages — fill a typed
ty_page_state(zif_ark_gui_state) and letzcl_ark_state_pagerender a Fiori-style page; no HTML in your ABAP code. Section kinds: KPI grid, table, form (framework read-back + required validation), chart, card grid, text, progress bars — plus one-call builders:table_section( )(any internal table, RTTI-derived columns) andchart_section( )(single bar/line series from ABAP data) - Convention Routing — no
CASEinon_event: an actionsave_orderis auto-dispatched to methodon_action_save_orderon your page class - Zero-Boilerplate Launcher —
ZARK_LAUNCHER(transaction-code entry for any page class) removes the host-screen boilerplate from every app report - Extensible JS Pipeline — see docs/js-extensions.md: upload any JS (three.js, ...) as a W3MI asset, register it, and ship your own widget components
- Zero External Dependencies — Runs entirely within SAP GUI using standard
CL_GUI_HTML_VIEWER
Create one report (SE38/ADT, e.g. ZMY_ARK_APP), paste this, activate, run:
REPORT zmy_ark_app.
CLASS lcl_page DEFINITION INHERITING FROM zcl_ark_gui_page FINAL.
PUBLIC SECTION.
METHODS on_event REDEFINITION .
PROTECTED SECTION.
METHODS build_html REDEFINITION .
ENDCLASS.
CLASS lcl_page IMPLEMENTATION.
METHOD build_html.
mo_html->add( |<h1>Hello ARK!</h1>| ).
" HTML rendering — fluent builder, any fragment in the page flow
mo_html->div(
iv_style = |border:1px solid #ddd; border-radius:8px;|
&& | padding:12px; max-width:640px;|
&& | background:#f8fafc|
iv_content = |<b>Plain HTML + styles</b>|
&& | mix freely with components.| ).
" Sized, colorful chart
DATA(lo_chart) = NEW zcl_ark_echarts(
iv_div_id = 'sales'
iv_width = '640px'
iv_height = 320 ).
lo_chart->set_title( 'Weekly Sales' ).
lo_chart->set_xaxis_categories( VALUE string_table(
( `Mon` ) ( `Tue` ) ( `Wed` ) ( `Thu` ) ( `Fri` ) ) ).
lo_chart->add_series(
iv_name = 'Revenue'
iv_type = 'bar'
iv_color_by_data = abap_true
it_data = VALUE zcl_ark_echarts=>ty_values(
( 120 ) ( 200 ) ( 150 ) ( 80 ) ( 270 ) ) ).
mo_html->add( lo_chart->render( ) ).
" sapevent link — handled in on_event below
mo_html->add_a( iv_txt = 'Open the full examples'
iv_act = 'run_examples' ).
ri_html = mo_html.
ENDMETHOD.
METHOD on_event.
CASE ii_event->mv_action.
WHEN 'run_examples'.
" Jump-run the bundled demo hub (own screen and GUI);
" returns here when the user exits it
SUBMIT zark_example AND RETURN.
TRY.
zcl_ark_gui=>get_instance( )->render( ).
CATCH zcx_ark_exception.
" Full-screen takeover may invalidate the viewer: rebuild
zcl_ark_gui=>get_instance( )->free( ).
zcl_ark_gui=>create( )->set_page( NEW lcl_page( ) ).
ENDTRY.
WHEN OTHERS.
rs_result = super->on_event( ii_event ).
ENDCASE.
ENDMETHOD.
ENDCLASS.
" --- Launcher: the HTML viewer needs a host screen (like abapGit) ---
SELECTION-SCREEN BEGIN OF SCREEN 1001.
SELECTION-SCREEN END OF SCREEN 1001.
START-OF-SELECTION.
TRY.
" Offline ECharts asset; falls back to CDN if absent
zcl_ark_echarts=>use_bundled_library( ).
CATCH zcx_ark_exception.
ENDTRY.
TRY.
zcl_ark_gui=>create( )->set_page( NEW lcl_page( ) ).
CALL SELECTION-SCREEN 1001.
CATCH zcx_ark_exception INTO DATA(lx_error).
MESSAGE lx_error TYPE 'E'.
ENDTRY.
AT SELECTION-SCREEN ON EXIT-COMMAND.
" F3 / Back / Escape: one level up the page stack (same as the on-page
" Back control); exit the program when already at the top level
IF sy-dynnr = 1001.
IF zcl_ark_gui=>get_instance( )->go_back( ) = abap_false.
zcl_ark_gui=>get_instance( )->free( ).
LEAVE PROGRAM.
ENDIF.
ENDIF.To navigate between your own pages from on_event, return the new page in the result:
WHEN 'nav_detail'.
rs_result-page = NEW lcl_detail_page( ).
rs_result-state = 1.Beyond the basics — the bundled demos are the living documentation. Run ZARK_EXAMPLE (SE38/SA38, or ZCL_ARK_EXAMPLE_APP as an ABAP application in ADT): its home page navigates to the form, table, chart, data-viewer (RTTI table + JSON tree), and browser-info examples — the last one probes which rendering engine (IE/MSHTML vs Edge/Chromium) the SAP GUI HTML viewer actually uses on your machine — and jump-runs the standalone demo reports. ZARK_ECHARTS_DEMO shows every charting mode (declarative API, override hatch, full option structure, themes); ZARK_SFLIGHT_DEMO is a database-driven dashboard on SFLIGHT/SCARR.
zcl_ark_echarts renders a chart as a plain HTML fragment, so it mixes freely with headings, tables, toolbars, or a second chart on the same page. Three usage levels, from simplest to most powerful:
1. Declarative API — type-safe, covers the common chart options:
DATA(lo_chart) = NEW zcl_ark_echarts( iv_div_id = 'chart1' iv_height = 420 ).
lo_chart->set_title( 'Stacked Area Chart' ).
lo_chart->set_xaxis_categories( VALUE string_table( ( `Mon` ) ( `Tue` ) ( `Wed` ) ) ).
lo_chart->add_series(
iv_name = 'Email'
it_data = VALUE zcl_ark_echarts=>ty_values( ( 120 ) ( 132 ) ( 101 ) )
iv_stack = 'Total'
iv_area = abap_true ).
mo_html->add( |<h1>Dashboard</h1>| ).
mo_html->add( lo_chart->render( ) ). " just one chunk in the page flow2. Override hatch — raw JSON shallow-merged (top-level keys) onto the generated option, for anything the declarative API does not cover yet (dataZoom, markLine, visualMap, ...):
lo_chart->set_option_override(
`{ "dataZoom": [{ "type": "slider" }, { "type": "inside" }] }` ).3. Full option structure — pass any ABAP structure as the complete ECharts option; serialized via /ui2/cl_json in camelCase mode (underscore field names become camelCase keys). Soft dependency: called dynamically, falls back to zcl_ark_json when /ui2/cl_json is not installed:
TYPES: BEGIN OF ty_pie_option,
title TYPE ty_title, " text / left
series TYPE tt_pie_series, " name / type / radius / data
END OF ty_pie_option.
lo_chart->set_option( ls_pie_option ).4. Chart click events — drill into any chart element (bar, pie slice, map region):
lo_chart->set_on_click( 'chart_drill' ).
" ... in the page's on_event:
WHEN 'chart_drill'.
DATA(lv_name) = ii_event->query( 'name' ). " category / datum name
DATA(lv_series) = ii_event->query( 'series' ). " series name
DATA(lv_value) = ii_event->query( 'value' ). " number (objects arrive as JSON string)
DATA(lv_idx) = CONV i( ii_event->query( 'idx' ) ).Query values are URL-decoded automatically (Chinese category names work). Single values are capped at 250 characters (percent-encoded Chinese ≈ 9 chars per character) — keep names short. On state pages, set chart_click_action on a chart section; its payload additionally carries chart (= section index) so multiple chart sections can share one action.
5. Maps (GeoJSON) — China map ships with the repository as MIME object ZARK_MAP_CHINA_JSON (abapGit W3MI, DataV 100000_full GeoJSON; use your own map data where licensing requires):
zcl_ark_echarts=>use_bundled_map( ). " session-level load (once)
lo_chart->set_map( 'china' ). " registers the map
lo_chart->set_visual_map( iv_min = '0' iv_max = '1200' ).
lo_chart->add_map_series(
iv_name = 'Sales'
it_data = VALUE zcl_ark_echarts=>tt_map_data(
( name = '广东' value = 1180 )
( name = '江苏' value = 1050 ) ) ). " names must match GeoJSON features
lo_chart->set_on_click( 'map_drill' ). " click a province -> sapeventCustom maps: upload any GeoJSON to SMW0 and load it with use_bundled_map( iv_map_name = 'usa' iv_mime_name = 'Z_USA_JSON' ), or pass the raw GeoJSON bytes directly: set_map( iv_name = 'usa' iv_xdata = zcl_ark_convert=>mime_to_xstring( 'Z_USA_JSON' ) ). The GeoJSON is served as a synchronous window.ARK_MAPS script through cache_asset; if the asset is missing the chart degrades to an empty map instead of a script error. State pages: set chart_map on a chart section and the map script is injected automatically.
Notes:
- ECharts 6.1.0 ships with the repository as MIME object
ZARK_ECHARTS_MIN_JS(abapGit W3MI, seesrc/assets/), so charts work offline out of the box;zcl_ark_echarts=>use_bundled_library( )loads it once per session and serves it throughcache_assetwith automatic CDN fallback. Passiv_include_lib = abap_falseto any additional chart on the same page. - The China map GeoJSON ships as MIME object
ZARK_MAP_CHINA_JSONthe same way (use_bundled_map( ), no CDN fallback — a missing asset degrades to an empty map). - Series data and categories are serialized with
zcl_ark_json=>to_json( ); pass plain ABAP internal tables, no string building required.add_seriesaccepts any internal table with a numeric row type —zcl_ark_echarts=>ty_valuesis the integer convenience type, declare your ownp/ftable for decimal values such as amounts.iv_color_by_data = abap_truecolors each datum from the palette (one color per bar/pie slice).iv_label = iv_label_thousands = abap_truerenders thousands-separated data labels (1,630,000). - To use a different ECharts version, replace the file in
src/assets/zark_echarts_min_js.w3mi.data.jsor pointc_cdn_urlat another CDN build. - Series data and categories are serialized with
zcl_ark_json=>to_json( ); pass plain ABAP internal tables, no string building required.add_seriesaccepts any internal table with a numeric row type —zcl_ark_echarts=>ty_valuesis the integer convenience type, declare your ownp/ftable for decimal values such as amounts.iv_color_by_data = abap_truecolors each datum from the palette (one color per bar/pie slice). - The constructor takes
iv_height(px) and optionaliv_width(e.g.'640px','50%'; default full width, centered when narrower). - If the library fails to load, the chart container shows a visible error banner instead of failing silently.
- See
ZCL_ARK_EXAMPLE_CHART_PAGE(mixed content, click drill-down), reportZARK_ECHARTS_DEMO(declarative API, override hatch, full-structure pie chart, dark theme, China map choropleth with province click), and reportZARK_SFLIGHT_DEMO(database-driven dashboard on the classic SFLIGHT/SCARR flight model) for usage.
Two generic components turn typed ABAP data into HTML without per-cell code. This is the answer to "I just want to show my data":
1. Any internal table → HTML table — RTTI-driven:
mo_html->add_table(
zcl_ark_html_table=>from_any_table( lt_any )->zif_ark_gui_renderable~render( ) ).Columns derive from the line type: components based on DDIC data elements contribute their medium/short/reptext label (field name as fallback), numeric columns are right-aligned, dates/times use the user format, deep table columns show their row count, .INCLUDE substructures are flattened. Values are HTML-escaped. The return value is the regular builder, so add_column( ), set_striped( ), ... still apply before render( ). Elementary line types render as a single TABLE_LINE column.
The generated table is interactive out of the box, entirely client-side (no sapevent round-trips): click a column header to sort ascending/descending — numeric and date columns sort by value, not by display text — and type into the filter bar above the table to match rows across all columns (the counter shows visible / total). Tables size to their content and scroll horizontally inside .ark-data-wrap instead of stretching the full page width. Manually built tables (plain add_column/add_cell) keep their previous behavior.
2. JSON string → collapsible tree — sXML-based, zero dependencies:
mo_html->add_table(
zcl_ark_json_tree=>create( lv_json )->set_open( abap_true
)->zif_ark_gui_renderable~render( ) ).Invalid JSON degrades to escaped plain text instead of a script error. set_open( ) controls the initial expand state of all nodes — flipping it and re-rendering is the expand-all/collapse-all pattern (a sapevent that sets rs_result-state = 1 without returning a new page re-renders in place). Styles come from zcl_ark_theme (ark-jt-* classes, token-driven); the tiny ES5 toggle script ships with the fragment and works on both MSHTML and Edge viewers.
See ZCL_ARK_EXAMPLE_DATA_PAGE (demo hub → Data Viewer) for both.
The shortest path from ABAP data to a working app page: inherit from zcl_ark_state_page, fill a typed state in build_state( ), and the framework renders the whole Fiori-style page — no HTML, no JSON, no CASE dispatcher in your code. Tables get sorting/filtering/CSV download and forms get POST read-back + required validation built in.
CLASS zcl_sales_page DEFINITION INHERITING FROM zcl_ark_state_page FINAL.
PUBLIC SECTION.
" Convention routing: action 'pick_month' -> method below (≤20 chars)
METHODS on_action_pick_month
IMPORTING ii_event TYPE REF TO zif_ark_gui_event
RETURNING VALUE(rs_result) TYPE zif_ark_gui_event_handler=>ty_handling_result
RAISING zcx_ark_exception .
PROTECTED SECTION.
METHODS build_state .
ENDCLASS.
CLASS zcl_sales_page IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
build_state( ).
ENDMETHOD.
METHOD build_state.
DATA(ls_state) = VALUE zif_ark_gui_state=>ty_page_state(
title = 'Sales Overview' subtitle = 'Typed state in, rendered page out' ).
" KPI cards — semantic colors drive the delta styling
ls_state-sections = VALUE #(
( kind = zif_ark_gui_state=>c_section_kind-kpi_grid
kpi_cards = VALUE #(
( title = 'Revenue' value = '4,286,000'
delta_text = '▲ 12.4% MoM'
delta_semantic = zif_ark_gui_state=>c_semantic-positive ) ) ) ).
" Chart — one call, single bar/line series from ABAP data
APPEND zcl_ark_state_page=>chart_section(
iv_title = 'Monthly Revenue'
iv_series_name = 'Revenue'
it_categories = VALUE string_table( ( `Jan` ) ( `Feb` ) ( `Mar` ) )
it_data = VALUE zcl_ark_echarts=>ty_values( ( 420 ) ( 455 ) ( 490 ) )
iv_click_action = 'pick_month' )
TO ls_state-sections.
" Table — any internal table; RTTI derives columns, DDIC labels, alignment
SELECT carrid, connid, price FROM sflight INTO TABLE @DATA(lt_flights) UP TO 20 ROWS.
APPEND zcl_ark_state_page=>table_section(
iv_title = 'Flight Details' it_data = lt_flights )
TO ls_state-sections.
" Form — POST values are read back into the state before the handler runs
ls_state-sections = VALUE #( BASE ls_state-sections
( kind = zif_ark_gui_state=>c_section_kind-form
form_action = 'apply_filter'
form_fields = VALUE #(
( input_type = 'text' label = 'Customer' name = 'customer' required = abap_true )
( input_type = 'submit' value = 'Apply' ) ) ) ).
set_state( ls_state ).
ENDMETHOD.
METHOD on_action_pick_month.
" ii_event->query( 'name' / 'value' / 'idx' ) — already URL-decoded
build_state( ). " rebuild state, e.g. with a filter applied
rs_result-state = 1. " re-render in place
ENDMETHOD.
ENDCLASS.Multi-series charts stay one call — pass a series table (per-series name/type/values; series without a type inherit iv_chart_type):
APPEND zcl_ark_state_page=>chart_section(
iv_title = 'Revenue vs Cost'
it_categories = VALUE string_table( ( `Jan` ) ( `Feb` ) )
it_series = VALUE zcl_ark_state_page=>tt_chart_series(
( name = 'Revenue' type = 'bar' values = VALUE #( ( `420` ) ( `455` ) ) )
( name = 'Cost' type = 'line' values = VALUE #( ( `300` ) ( `280.5` ) ) ) ) )
TO ls_state-sections.Progress bars are a section kind too — label + right-aligned value text + semantic fill color (percent is clamped to 0-100 by the renderer):
" Progress — account validity with used/total days
APPEND VALUE zif_ark_gui_state=>ty_section(
kind = zif_ark_gui_state=>c_section_kind-progress
title = 'Account Validity'
progress_items = VALUE #(
( label = '2025-01-01 ~ 2026-12-31'
value_text = 'Used 620 / 730 days'
percent = 85
semantic = zif_ark_gui_state=>c_semantic-positive ) )
) TO ls_state-sections.Advanced charts (stacked options, maps, pies) build their option with the zcl_ark_echarts declarative API and pass lo_chart->get_option_json( ) into the section's chart_option — see ZCL_ARK_EXAMPLE_STATE_PAGE (demo hub → State Page) for every section kind in one page.
Launch the page with zero boilerplate: create a transaction code pointing at report ZARK_LAUNCHER, set the start parameter P_PAGE = ZCL_SALES_PAGE ("skip initial screen"), and run it — no host-screen SELECTION-SCREEN copy-paste needed.
zcl_ark_template keeps HTML/JS skeletons out of your ABAP code. Placeholders use {{NAME}} syntax; unfilled placeholders are kept as-is so omissions are easy to spot:
mo_html->add(
zcl_ark_template=>create( `<h1>{{TITLE}}</h1>` " or =>from_mime( 'ZARK_MY_TEMPLATE' )
)->set( iv_name = 'TITLE' iv_value = 'Hello'
)->render( ) ).Templates can be stored as text files in the MIME repository (SMW0) and loaded with zcl_ark_template=>from_mime( ).
The reference development base is the official SAP ABAP Platform 2023 developer trial, running as a local Docker container. On the client side, ARK targets the Edge (Chromium/WebView2) rendering engine of CL_GUI_HTML_VIEWER — SAP GUI 7.70+ with the Edge kernel enabled (see the browser-info example in ZARK_EXAMPLE to probe your actual engine). Legacy IE-engine clients are not supported.
| Item | Value |
|---|---|
| System | Local Docker container a4h, official image sapse/abap-cloud-developer-trial:2023 |
| Platform | ABAP Platform 2023 (SAP_BASIS 7.57, 2023 trial, image tag VA_ABAP_PL_2023HDB) |
| Kernel | 7.93 (793_REL, built 2024-08, SLES 15 x86_64) |
| Database | SAP HANA (built into the container, port 30213) |
- Install via abapGit — paste this repository URL
- Or manually copy all
src/**/*.abapfiles into your SAP system - Activate all classes and interfaces
- Run the demo program
ZARK_EXAMPLEfrom SE38/SA38, or run classZCL_ARK_EXAMPLE_APPas an ABAP application in ADT
src/
├── core/ # Tier 1: Core Engine
│ ├── zcl_ark_html # HTML builder
│ ├── zcl_ark_gui # GUI controller
│ ├── zcl_ark_html_viewer_gui # HTML viewer wrapper
│ ├── zcl_ark_gui_event # Event object
│ ├── zcl_ark_convert # Conversion utilities
│ ├── zcl_ark_json # JSON serializer (sXML based)
│ ├── zcl_ark_theme # Fiori design tokens + default CSS
│ └── interfaces...
├── framework/ # Tier 2: Page Framework
│ ├── zcl_ark_gui_page # Page base class
│ ├── zcl_ark_gui_component # Component base class
│ ├── zcl_ark_state_page # Declarative state page (renders ty_page_state)
│ └── zif_ark_gui_state # Declarative page state schema (draft)
├── components/ # Tier 3: UI Components
│ ├── zcl_ark_html_form # Form builder
│ ├── zcl_ark_html_table # Table builder (+ from_any_table RTTI rendering)
│ ├── zcl_ark_html_toolbar # Toolbar builder
│ ├── zcl_ark_echarts # ECharts chart component
│ ├── zcl_ark_json_tree # JSON string -> collapsible tree
│ └── zcl_ark_template # Text template with placeholders
├── assets/ # Static assets (MIME objects)
│ ├── zark_echarts_min_js # Apache ECharts 6.1.0 bundle
│ └── zark_map_china_json # China map GeoJSON (DataV 100000_full)
└── examples/ # Demo applications
MIT License — same as abapGit.
Built with by extracting the best from abapGit.