codecov-io: ## [Current coverage](https://codecov.io/gh/napjon/krisk/pull/47?src=pr) is 93.40% (diff: 100%)
> Merging [#47](https://codecov.io/gh/napjon/krisk/pull/47?src=pr) into [0.2-develop](https://codecov.io/gh/napjon/krisk/branch/0.2-develop?src=pr) will increase coverage by **0.31%**

```diff
@@           0.2-develop        #47   diff @@
=============================================
  Files               10          9     -1   
  Lines              333        349    +16   
  Methods              0          0          
  Messages             0          0          
  Branches            48         49     +1   
=============================================
+ Hits               310        326    +16   
  Misses              14         14          
  Partials             9          9          
```

> Powered by [Codecov](https://codecov.io?src=pr). Last update [f873a68...37ff13d](https://codecov.io/gh/napjon/krisk/compare/f873a68da47b99677a73ebea2d92698db482a6d0...37ff13d06b37388e99685b3af43d00c8ff0eb429?src=pr)
diff --git a/krisk/chart.py b/krisk/chart.py
index b5d17a1..beb692d 100644
--- a/krisk/chart.py
+++ b/krisk/chart.py
@@ -3,20 +3,37 @@
 import uuid
 import json
 from copy import deepcopy
-from krisk.template import *
 from krisk.connections import get_paths
 from IPython.display import Javascript
-from krisk.util import get_content
-
+from krisk.util import get_content, join_current_dir
+
+JS_TEMPLATE_PATH = 'static/krisk.js'
+EVENT_TEMPLATE_PATH = 'static/on_event.js'
+HTML_TEMPLATE_PATH = 'static/template.html'
+
+APPEND_ELEMENT = """
+$('#{id}').attr('id','{id}'+'_old');
+element.append('<div id="{id}" style="width: {width}px;height:{height}px;"></div>');"""
+
+OPTION_TEMPLATE = {
+    'title': {
+        'text': ''
+    },
+    'tooltip': {'axisPointer': {'type': ''}},
+    'legend': {
+        'data': []
+    },
+    'xAxis': {
+        'data': []
+    },
+    'yAxis': {},
+    'series': []
+}
 
 
 class Chart(object):
     """Chart Object"""
 
-    JS_TEMPLATE_PATH = 'static/krisk.js'
-    EVENT_TEMPLATE_PATH = 'static/on_event.js'
-
-
     def __init__(self, **kwargs):
         """Constructor"""
         # Currently, there are three type of data structure.
@@ -363,6 +380,65 @@ class Chart(object):
             'yAxis'], self.option['xAxis']
         return self
 
+    def _set_label_axes(self, xy, **kwargs):
+        """Set label axes name and other customization"""
+        assert xy in ['x','y']
+        self.option[xy + 'Axis'].update(**kwargs)
+        return self
+
+    def set_xlabel(self, name, axis_position='middle', axis_gap=30, rotation=0, font_size=16):
+        """Set x-axis label and other type of customization.
+
+        Parameters
+        ----------
+        name: the label of axes
+        axis_position: {start, middle, end}, default middle
+            horizontal alignment of label. start will position the label at leftmost position
+        axis_gap: int default 30
+            vertical alignment position of label. zero start from x-axis and going further away
+        rotation: int default 0
+            the rotation of the label
+        font_size: int default 16
+            the font size of the label
+
+        Return
+        ------
+        Chart object
+        """
+        label_kwargs = dict(name=name,
+                            nameLocation=axis_position,
+                            nameGap=axis_gap,
+                            nameTextStyle={'fontSize':font_size},
+                            nameRotate=rotation)
+        return self._set_label_axes('x', **label_kwargs)
+
+    def set_ylabel(self, name, axis_position='middle', axis_gap=30, rotation=90, font_size=16):
+        """Set y-axis label and other type of customization.
+        
+        Parameters
+        ----------
+        name: the label of axes
+        axis_position: {start, middle, end}, default middle
+            vertical alignment of label. start will position the label at bottom position
+        axis_gap: int default 30
+            horizontal alignment position of label. zero start from y-axis and going further 
+            away
+        rotation: int default 90
+            the rotation of the label
+        font_size: int default 16
+            the font size of the label
+
+        Return
+        ------
+        Chart object
+        """
+        label_kwargs = dict(name=name,
+                            nameLocation=axis_position,
+                            nameGap=axis_gap,
+                            nameTextStyle={'fontSize':font_size},
+                            nameRotate=rotation)
+        return self._set_label_axes('y', **label_kwargs)
+
     # ------------------------------------------------------------------------------------------------
     # Events
     def on_event(self, event, handler):
@@ -436,8 +512,8 @@ class Chart(object):
     def _get_resync_option_strings(self, option):
         """Resync Chart option"""
 
-        js_template = get_content(self.JS_TEMPLATE_PATH)
-        event_template = get_content(self.EVENT_TEMPLATE_PATH)
+        js_template = get_content(JS_TEMPLATE_PATH)
+        event_template = get_content(EVENT_TEMPLATE_PATH)
 
         events = [event_template.format(
             event=e, function=self._events[e]) for e in self._events]
@@ -474,4 +550,12 @@ class Chart(object):
     def to_html(self, path):
         "Save full html file"
         # TODO: Optional add open new tab as soon as it save the html file
-        save_html(self._repr_javascript_(), path)
+        from jinja2 import Template
+
+        script = self._repr_javascript_()
+        script = script.replace('element', '$("body")')
+
+        html_template = Template(get_content(HTML_TEMPLATE_PATH))
+        html_content = html_template.render(SCRIPT=script)
+        with open(path, 'w') as f:
+            f.write(html_content)
diff --git a/krisk/plot/make_chart.py b/krisk/plot/make_chart.py
index ce77d49..ea90926 100644
--- a/krisk/plot/make_chart.py
+++ b/krisk/plot/make_chart.py
@@ -39,6 +39,10 @@ def make_chart(df, **kwargs):
     chart = Chart(**kwargs)
     chart._kwargs_chart_['data_columns'] = df.columns
 
+    chart.set_xlabel(kwargs['x'])
+    if kwargs.get('y', None):
+        chart.set_ylabel(kwargs['y'])
+
     if kwargs['type'] in ['bar', 'line', 'hist']:
         set_bar_line_chart(chart, df, **kwargs)
 
diff --git a/krisk/template.py b/krisk/template.py
deleted file mode 100644
index b1b2e19..0000000
--- a/krisk/template.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from krisk.util import join_current_dir
-
-
-def save_html(script, path):
-
-    from jinja2 import Template
-
-    html_template = open(join_current_dir('static/template.html'), 'r')
-    script = script.replace('element', '$("body")')
-    f = open(path, 'w')
-    f.write(Template(html_template.read()).render(SCRIPT=script))
-    f.close()
-    html_template.close()
-
-
-APPEND_ELEMENT = """
-$('#{id}').attr('id','{id}'+'_old');
-element.append('<div id="{id}" style="width: {width}px;height:{height}px;"></div>');"""
-
-OPTION_TEMPLATE = {
-    'title': {
-        'text': ''
-    },
-    'tooltip': {'axisPointer': {'type': ''}},
-    'legend': {
-        'data': []
-    },
-    'xAxis': {
-        'data': []
-    },
-    'yAxis': {},
-    'series': []
-}

Set Style for y and x axis
diff --git a/krisk/tests/conftest.py b/krisk/tests/conftest.py
index 4d132f1..bbf3e2e 100644
--- a/krisk/tests/conftest.py
+++ b/krisk/tests/conftest.py
@@ -21,6 +21,14 @@ def gapminder():
         DATA_DIR + '/gapminderDataFiveYear.txt', sep='\t').groupby(
             ['year', 'continent'], as_index=False).first())
 
+@pytest.fixture(scope="module")
+def decl_chart():
+    "Declarative Chart"
+    from krisk.chart import Chart
+    chart = Chart()
+    chart.option['series'] = [{'data': [10, 3, 7, 4, 5], 'name': 'continent', 'type': 'bar'}]
+    chart.option['xAxis'] =  {'data': ['Americas', 'Asia', 'Africa', 'Oceania', 'Europe']}
+    return chart
 
 @pytest.fixture
 def gap_chart(gapminder):
diff --git a/krisk/tests/test_chart.py b/krisk/tests/test_chart.py
index 88c1d9f..9376e6f 100644
--- a/krisk/tests/test_chart.py
+++ b/krisk/tests/test_chart.py
@@ -48,3 +48,19 @@ def test_color(bar_simple):
         background='green', palette=['purple']).get_option()
     assert colored['backgroundColor'] == 'green'
     assert colored['color'] == ['purple']
+
+def test_label_axes(decl_chart):
+    decl_chart.set_xlabel('xlabel')
+    decl_chart.set_ylabel('ylabel')
+    assert decl_chart.option['xAxis'] == {'data': ['Americas', 'Asia', 'Africa', 'Oceania', 'Europe'],
+                                            'name': 'xlabel',
+                                            'nameGap': 30,
+                                            'nameLocation': 'middle',
+                                            'nameRotate': 0,
+                                            'nameTextStyle': {'fontSize': 16}}
+
+    assert decl_chart.option['yAxis'] == {'name': 'ylabel',
+                                           'nameGap': 30,
+                                           'nameLocation': 'middle',
+                                           'nameRotate': 90,
+                                           'nameTextStyle': {'fontSize': 16}}
\ No newline at end of file
diff --git a/krisk/tests/test_plot.py b/krisk/tests/test_plot.py
index 08cf413..ebbd7f5 100644
--- a/krisk/tests/test_plot.py
+++ b/krisk/tests/test_plot.py
@@ -5,6 +5,16 @@ DATA_DIR = 'krisk/tests/data'
 
 read_option_tests = lambda f: json.load(open(DATA_DIR + '/' + f, 'r'))
 
+
+# Will delete this in 0.3, https://github.com/napjon/krisk/issues/46
+def remove_name_label(chart):
+    remove_key = lambda s,prefix: dict(filter(lambda t: not t[0].startswith(prefix),
+                             chart.option[s].items()))
+    chart.option['xAxis'] = remove_key('xAxis', 'name')
+    chart.option['yAxis'] = remove_key('yAxis', 'name')
+    return chart
+
+
 def test_bar(gapminder):
 
     #Bar
@@ -16,12 +26,12 @@ def test_bar(gapminder):
                how='mean',
                stacked=True,
                annotate=True)
-    assert p.get_option() == true_option
+    assert remove_name_label(p).get_option() == true_option
 
     #Bar with x-axis and category
     true_option = read_option_tests('bar_x_c.json')
     p = kk.bar(gapminder,'year',c='continent',stacked=True)
-    assert p.get_option() == true_option
+    assert remove_name_label(p).get_option() == true_option
 
 
     # Bar Annotate All
@@ -33,10 +43,10 @@ def test_bar(gapminder):
                how='mean',
                stacked=True,
                annotate='all')
-    assert p.get_option() == true_option
+    assert remove_name_label(p).get_option() == true_option
 
     p = kk.bar(gapminder,'continent',y='gdpPercap',how='mean')
-    assert p.get_option() == {'legend': {'data': []},
+    assert remove_name_label(p).get_option() == {'legend': {'data': []},
                              'series': [{'data': [4426.026, 8955.554, 802.675, 3255.367, 19980.596],
                                'name': 'continent',
                                'type': 'bar'}],
@@ -48,10 +58,10 @@ def test_bar(gapminder):
 def test_trendline(gapminder):
 
     p = kk.bar(gapminder,'year',how='mean',y='pop',trendline=True)
-    assert p.get_option() == read_option_tests('bar_year_pop_mean_trendline.json')
+    assert remove_name_label(p).get_option() == read_option_tests('bar_year_pop_mean_trendline.json')
 
     p = kk.bar(gapminder,'year',how='mean',y='pop',trendline=True,c='continent',stacked=True)
-    assert p.get_option() == read_option_tests('bar_year_pop_mean_continent_trendline.json')
+    assert remove_name_label(p).get_option() == read_option_tests('bar_year_pop_mean_continent_trendline.json')
 
     try:
         kk.bar(gapminder,'year',how='mean',y='pop',trendline=True,c='continent')
@@ -71,7 +81,7 @@ def test_line(gapminder):
         area=True,
         annotate='all')
 
-    assert p.get_option() == true_option
+    assert remove_name_label(p).get_option() == true_option
 
 def test_smooth_line(gapminder):
 
@@ -91,14 +101,14 @@ def test_full_bar_line(gapminder):
 
     true_option = read_option_tests('full_bar_line.json')
 
-    assert bar.option == line.option == true_option
+    assert remove_name_label(bar).option == remove_name_label(line).option == true_option
 
 
 def test_hist(gapminder):
 
     true_option  = read_option_tests('hist_x.json')
     p = kk.hist(gapminder,'lifeExp',bins=10)
-    assert p.get_option() == true_option
+    assert remove_name_label(p).get_option() == true_option
 
 
     true_option = read_option_tests('hist.json')
@@ -110,12 +120,13 @@ def test_hist(gapminder):
         normed=True,
         stacked=True)
 
-    assert p.get_option() == true_option
+    assert remove_name_label(p).get_option() == true_option
 
 
 def test_density(gapminder):
 
-    option = kk.hist(gapminder,'lifeExp',density=True).get_option()
+    chart1 = kk.hist(gapminder,'lifeExp',density=True)
+    option = remove_name_label(chart1).get_option()
 
     assert option['series'][0]['data'] == [0, 4, 2, 7, 2, 2, 3, 5, 13, 16, 6]
     assert option['series'][-1] == {'data': [0, 4, 2, 7, 2, 2, 3, 5, 13, 16, 6, 0],
@@ -127,7 +138,8 @@ def test_density(gapminder):
                                'data': [0, 28, 34, 39, 44, 49, 55, 60, 65, 70, 75, 81, 0]}
 
     true_option = read_option_tests('hist_lifeExp_b10_continent_density.json')
-    option2 = kk.hist(gapminder,'lifeExp',bins=10,c='continent',stacked=True,density=True).get_option()
+    chart = kk.hist(gapminder,'lifeExp',bins=10,c='continent',stacked=True,density=True)
+    option2 = remove_name_label(chart).get_option()
     assert true_option == option2
 
     try:

pip install -e .
parse_log_pytest
requirements.txt
coverage pytest
3.5
requirements.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
anyio==3.6.2
argon2-cffi==21.3.0
argon2-cffi-bindings==21.2.0
async-generator==1.10
attrs==22.2.0
Babel==2.11.0
backcall==0.2.0
bleach==4.1.0
certifi==2021.5.30
cffi==1.15.1
charset-normalizer==2.0.12
comm==0.1.4
contextvars==2.4
coverage==6.2
dataclasses==0.8
decorator==5.1.1
defusedxml==0.7.1
entrypoints==0.4
idna==3.10
immutables==0.19
importlib-metadata==4.8.3
iniconfig==1.1.1
ipykernel==5.5.6
ipython==7.16.3
ipython-genutils==0.2.0
ipywidgets==7.8.5
jedi==0.17.2
Jinja2==3.0.3
json5==0.9.16
jsonschema==3.2.0
jupyter==1.1.1
jupyter-client==7.1.2
jupyter-console==6.4.3
jupyter-core==4.9.2
jupyter-server==1.13.1
jupyterlab==3.2.9
jupyterlab-pygments==0.1.2
jupyterlab-server==2.10.3
jupyterlab_widgets==1.1.11
-e git+https://github.com/napjon/krisk.git@f873a68da47b99677a73ebea2d92698db482a6d0#egg=krisk
MarkupSafe==2.0.1
mistune==0.8.4
nbclassic==0.3.5
nbclient==0.5.9
nbconvert==6.0.7
nbformat==5.1.3
nest-asyncio==1.6.0
notebook==6.4.10
numpy==1.19.5
packaging==21.3
pandas==1.1.5
pandocfilters==1.5.1
parso==0.7.1
pexpect==4.9.0
pickleshare==0.7.5
pluggy==1.0.0
prometheus-client==0.17.1
prompt-toolkit==3.0.36
ptyprocess==0.7.0
py==1.11.0
pycparser==2.21
Pygments==2.14.0
pyparsing==3.1.4
pyrsistent==0.18.0
pytest==7.0.1
python-dateutil==2.9.0.post0
pytz==2025.2
pyzmq==25.1.2
requests==2.27.1
Send2Trash==1.8.3
six==1.17.0
sniffio==1.2.0
terminado==0.12.1
testpath==0.6.0
tomli==1.2.3
tornado==6.1
traitlets==4.3.3
typing_extensions==4.1.1
urllib3==1.26.20
wcwidth==0.2.13
webencodings==0.5.1
websocket-client==1.3.1
widgetsnbextension==3.6.10
zipp==3.6.0

name: krisk
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - anyio==3.6.2
      - argon2-cffi==21.3.0
      - argon2-cffi-bindings==21.2.0
      - async-generator==1.10
      - attrs==22.2.0
      - babel==2.11.0
      - backcall==0.2.0
      - bleach==4.1.0
      - cffi==1.15.1
      - charset-normalizer==2.0.12
      - comm==0.1.4
      - contextvars==2.4
      - coverage==6.2
      - dataclasses==0.8
      - decorator==5.1.1
      - defusedxml==0.7.1
      - entrypoints==0.4
      - idna==3.10
      - immutables==0.19
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - ipykernel==5.5.6
      - ipython==7.16.3
      - ipython-genutils==0.2.0
      - ipywidgets==7.8.5
      - jedi==0.17.2
      - jinja2==3.0.3
      - json5==0.9.16
      - jsonschema==3.2.0
      - jupyter==1.1.1
      - jupyter-client==7.1.2
      - jupyter-console==6.4.3
      - jupyter-core==4.9.2
      - jupyter-server==1.13.1
      - jupyterlab==3.2.9
      - jupyterlab-pygments==0.1.2
      - jupyterlab-server==2.10.3
      - jupyterlab-widgets==1.1.11
      - markupsafe==2.0.1
      - mistune==0.8.4
      - nbclassic==0.3.5
      - nbclient==0.5.9
      - nbconvert==6.0.7
      - nbformat==5.1.3
      - nest-asyncio==1.6.0
      - notebook==6.4.10
      - numpy==1.19.5
      - packaging==21.3
      - pandas==1.1.5
      - pandocfilters==1.5.1
      - parso==0.7.1
      - pexpect==4.9.0
      - pickleshare==0.7.5
      - pluggy==1.0.0
      - prometheus-client==0.17.1
      - prompt-toolkit==3.0.36
      - ptyprocess==0.7.0
      - py==1.11.0
      - pycparser==2.21
      - pygments==2.14.0
      - pyparsing==3.1.4
      - pyrsistent==0.18.0
      - pytest==7.0.1
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - pyzmq==25.1.2
      - requests==2.27.1
      - send2trash==1.8.3
      - six==1.17.0
      - sniffio==1.2.0
      - terminado==0.12.1
      - testpath==0.6.0
      - tomli==1.2.3
      - tornado==6.1
      - traitlets==4.3.3
      - typing-extensions==4.1.1
      - urllib3==1.26.20
      - wcwidth==0.2.13
      - webencodings==0.5.1
      - websocket-client==1.3.1
      - widgetsnbextension==3.6.10
      - zipp==3.6.0
prefix: /opt/conda/envs/krisk

krisk/tests/test_chart.py::test_label_axes
krisk/tests/test_chart.py::test_replot_and_resync krisk/tests/test_chart.py::test_flip krisk/tests/test_chart.py::test_read_df krisk/tests/test_chart.py::test_color krisk/tests/test_plot.py::test_bar krisk/tests/test_plot.py::test_trendline krisk/tests/test_plot.py::test_line krisk/tests/test_plot.py::test_smooth_line krisk/tests/test_plot.py::test_full_bar_line krisk/tests/test_plot.py::test_hist krisk/tests/test_plot.py::test_density krisk/tests/test_plot.py::test_scatter
krisk/plot/make_chart.py krisk/chart.py krisk/template.py
krisk/plot/make_chart.py krisk/chart.py krisk/template.py